[小ネタ]CodePipeline上でシンボリックリンクを利用していたコードが想定外の動きをした話

2020.05.29

こんにちは!DA(データアナリティクス)事業本部 インテグレーション部の大高です。

先日ハマった「CodePipelineを利用した場合にシンボリックリンクが想定外の動きをすることを知った」という話の紹介です。

何が起きたのか?

CodeCommit、CodeBuild、CodePipelineを利用しています。また、CodeCommit上にはsymlink(シンボリックリンク)でパスを通したmoduleの参照をしているPythonのソースコードがコミットされています。

CodePipelineでこのソースコードに対するテストコードを動かそうとしたした際に、ローカル環境では問題なくテストコードが動くのに、CodePipeline上ではmoduleが見つからない旨のビルドエラーが起きている、という話を聞いて調査してみたのが発端です。

# bar-linkがsymlink。bar-linkが見つからない旨のエラーが出る。
from bar-link import foo

ビルドエラー時のlsコマンドのログを見てみると、どうもsymlinkが実体を持っていそうに見えるということが分かった時点で、下記の記事を見つけました。

今回は、どうもこのケースに当てはまりそうだったので、こちらの記事を参考にさせて頂き、検証を行ってみました。

検証してみた

CodeCommit

簡単に以下のようなソースを用意して、CodeCommit上に適当に作成したリポジトリにコミットします。

foo
┗ bar
    ┗ sample.txt
bar-link
README.md

ソースの構成は上記の通りで、bar-linkが以下のように作成したシンボリックリンクになります。

$ ln -s ./foo/bar bar-link

lsをするとこんな感じです。

$ ls -al
total 0
drwxrwxrwx 1 foo bar 4096 May 27 22:12 .
drwxrwxrwx 1 foo bar 4096 May 27 22:09 ..
drwxrwxrwx 1 foo bar 4096 May 27 22:13 .git
-rw-rw-rw- 1 foo bar   29 May 27 22:11 README.md
lrwxrwxrwx 1 foo bar    9 May 27 22:12 bar-link -> ./foo/bar
drwxrwxrwx 1 foo bar 4096 May 27 22:11 foo

CodeBuild

上記で用意したリポジトリを参照するビルドプロジェクトを作成し、Buildspecは以下のようにしてみます。

version: 0.2

phases:
  build:
    commands:
        - echo 'Hello, CodeBuild.'
        - ls -al
        - cat bar-link

一旦、この状態でビルドを行ってビルドログを見てみます。

[Container] 2020/05/29 01:04:33 Running command echo 'Hello, CodeBuild.'
Hello, CodeBuild.

[Container] 2020/05/29 01:04:33 Running command ls -al
total 20
drwxr-xr-x 4 root root 4096 May 29 01:04 .
drwxr-xr-x 3 root root 4096 May 29 01:04 ..
drwxr-xr-x 4 root root 4096 May 29 01:04 .git
-rw-r--r-- 1 root root   29 May 29 01:04 README.md
lrwxrwxrwx 1 root root    9 May 29 01:04 bar-link -> ./foo/bar
drwxr-xr-x 3 root root 4096 May 29 01:04 foo

[Container] 2020/05/29 01:04:33 Running command cat bar-link
cat: bar-link: Is a directory

[Container] 2020/05/29 01:04:33 Command did not exit successfully cat bar-link exit status 1
[Container] 2020/05/29 01:04:33 Phase complete: BUILD State: FAILED
[Container] 2020/05/29 01:04:33 Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command: cat bar-link. Reason: exit status 1

bar-linkはsymlinkのままで、catした際にエラーになっていることが分かります。ここまでは普通ですね。

CodePipeline

ソースステージ、ビルドステージに上記のものを指定したパイプラインを作成します。デプロイステージは今回不要なのでスキップします。

パイプラインができたら、早速「変更をリリースする」をクリックしてパイプラインを流してみます。すると、先程は失敗したビルドがうまく流れているようです。

ビルドログを見てみると以下のようになっています。

[Container] 2020/05/29 01:32:34 Entering phase BUILD
[Container] 2020/05/29 01:32:34 Running command echo 'Hello, CodeBuild.'
Hello, CodeBuild.

[Container] 2020/05/29 01:32:34 Running command ls -al
total 20
drwxr-xr-x 3 root root 4096 May 29 01:32 .
drwxr-xr-x 3 root root 4096 May 29 01:32 ..
-rw-r--r-- 1 root root   29 May 29 01:32 README.md
-rw-rw-rw- 1 root root    9 May 29 01:32 bar-link
drwxr-xr-x 3 root root 4096 May 29 01:32 foo

[Container] 2020/05/29 01:32:34 Running command cat bar-link
./foo/bar
[Container] 2020/05/29 01:32:34 Phase complete: BUILD State: SUCCEEDED

symlinkだったものがテキストになっており、中身はリンクのパスである./foo/barという文字列になっていることが分かります。

これは、以下の記事のデリバリの流れの図が分かりやすかったのですが、一度ソースコードをInputArtifactとしてS3へ格納しているために、テキストになったのだと考えられます。

回避策としては、BuildSpec内で一度該当ファイルを削除してsymlinkを作成し直すか、実ファイルを配置するかをすれば良さそうですね。

まとめ

symlinkを使ったコードを、CodePipelineで利用する際にはちょっと気を付ける必要がありますね。

どなたかのお役に立てば幸いです。それでは!