Jenkinsの抽象化されたjava.io.Fileである FilePath?

ビルドのジョブが動くノード*1によって、ワークスペースのパスは異なっていたりします。なのでJenkinsのファイルシステムはjava.io.Fileに似た FilePath?というパスが抽象化されたクラスを使用します。以下サンプルコード。

   @Override
   public boolean perform(AbstractBuild build, Launcher launcher,
           BuildListener listener) throws InterruptedException, IOException {
 
       FilePath root = build.getModuleRoot(); // ワークスペースのルート.スレーブでビルドが動くと、他サーバのディレクトリだったりする。
       FilePath fromFile = new FilePath(root, "from.txt");
       System.out.println(fromFile);

       FilePath toFile = new FilePath(new FilePath(build.getRootDir()),
               "to.txt"); // ココはビルドのルートで、必ずMasterサーバ上にある
       System.out.println(toFile);
       fromFile.copyTo(toFile); // こんな感じにノード間でコピーできる。
        // (このビルドがスレーブで動く場合、スレーブのワークスペースのファイルが、マスターサーバにコピーされたりする。)
       return true;
   }

このように動いているサーバを抽象化し、ノード間のコピーもシームレスにできたりします。便利ですね。。

実行結果は

[省略]/workspace/hoge/work/jobs/Test/workspace/from.txt
[省略]/workspace/hoge/work/jobs/Test/builds/2015-09-25_13-12-45/to.txt

となります。これはマスタで動いてしまいましたが、一行目はワークスペースなので場合によっては別ノードのパスになります。逆に二行目はビルドパスなので必ずマスターサーバのパスになります。

ファイル出力すると

プラグインからファイルを出力したいときがありますが、たとえば

File file = new File("/tmp/fullpath.txt");
file.createNewFile();

などと書くと、ファイルの書き出し先は、マスターサーバの上記のディレクトリになります。 そのビルドがスレーブで実行されていても、です。これを、スレーブで実行されたらファイルはスレーブに書き出したいケースでは、先のFilePath?FileCallable?インタフェースを使用します。

ファイルシステムを抽象化した仕組みである FilePath? クラスですが、これは

FilePath moduleRoot = build.getModuleRoot(); ←そのプロジェクトのWorkSpaceのRoot

などとして参照を得ることができますが、ここにはそのプロジェクトが実行されたときのワークスペースへのパスが格納されています。 マスターで実行したときとスレーブで実行したときに違う値が格納されているというわけですね。。

FileCallable?インタフェースは 環境に応じたFileクラスへの参照を持っていて、そのインタフェース経由でファイル操作を行うことができる仕組みです。

実際にファイル出力するサンプルプログラムを作ってみました。FileCallable?インタフェースの

public String[] invoke(File f, VirtualChannel channel);

メソッドに渡されてくる引数 f がFilePath?が指している場所への参照になっているわけですね。。

サンプルコード

サンプルコード:

FilePath moduleRoot = build.getModuleRoot();
File rootDir = build.getRootDir();
FilePath rootDirPath = new FilePath(rootDir);
FilePath workspace = build.getWorkspace();
File artifactsDir = build.getArtifactsDir();
FilePath artifactsDirPath = new FilePath(artifactsDir);

listener.getLogger().println(moduleRoot);
listener.getLogger().println(rootDir);
listener.getLogger().println(rootDirPath);
listener.getLogger().println(workspace);
listener.getLogger().println(artifactsDir);
listener.getLogger().println(artifactsDirPath);

try {
    // Fileを返すメソッドなので、マスターのディレクトリと予測
    File file = new File(build.getRootDir(), "buildDir.txt");
    listener.getLogger().println(file.getAbsolutePath());
    file.createNewFile();

     // おなじくFileを操作しているので、マスターのディレクトリと予測
    File file2 = new File("/tmp/fullpath.txt");
    listener.getLogger().println(file2.getAbsolutePath());
    file2.createNewFile();

    moduleRoot = build.getModuleRoot();
    String[] result = moduleRoot.act(new F());
    listener.getLogger().println(result[0]);
    listener.getLogger().println(result[1]);
} catch (IOException e) {
    e.printStackTrace();
} catch (InterruptedException e) {
    e.printStackTrace();
}
private static class F implements FileCallable<String[]> {
    private static final long serialVersionUID = 1L;

    public String[] invoke(File f, VirtualChannel channel)
            throws IOException, InterruptedException {
        File file = new File(f, "root.txt");    // f はFilePathから生成されるので場合によってスレーブのパスとなると予測
        file.createNewFile();

        File file2 = new File("/tmp/fullpath2.txt"); // このメソッド内はスレーブのパスになると予測
        file2.createNewFile();

        return new String[] { file.getAbsolutePath(),
                file2.getAbsolutePath() };
    }
}

について、マスターとスレーブで実行した結果は以下のようになります。

マスター:

Started by user anonymous
Building on master in workspace /var/lib/jenkins/workspace/Test3
/var/lib/jenkins/workspace/Test3/var/lib/jenkins/workspace/Test3
/var/lib/jenkins/jobs/Test3/builds/5
/var/lib/jenkins/jobs/Test3/builds/5
/var/lib/jenkins/workspace/Test3
/var/lib/jenkins/jobs/Test3/builds/5/archive
/var/lib/jenkins/jobs/Test3/builds/5/archive

/var/lib/jenkins/jobs/Test3/builds/5/buildDir.txt
/tmp/fullpath.txt
/var/lib/jenkins/workspace/Test3/root.txt
/tmp/fullpath2.txt
Finished: SUCCESS

スレーブ:

Started by user anonymous
Building remotely on www (Linux) in workspace /opt/jenkins/workspace/Test3
/opt/jenkins/workspace/Test3/opt/jenkins/workspace/Test3   <- slave
/var/lib/jenkins/jobs/Test3/builds/4
/var/lib/jenkins/jobs/Test3/builds/4
/opt/jenkins/workspace/Test3        <- slave
/var/lib/jenkins/jobs/Test3/builds/4/archive
/var/lib/jenkins/jobs/Test3/builds/4/archive

/var/lib/jenkins/jobs/Test3/builds/4/buildDir.txt  <- slaveで実行されたがマスタに出力
/tmp/fullpath.txt           <- slaveで実行されたがマスタに出力
/opt/jenkins/workspace/Test3/root.txt       <- slaveに出力
/tmp/fullpath2.txt        <- slaveに出力
Finished: SUCCESS

このようにFileの参照を返すメソッドについてはスレーブで実行してもファイルパスはマスターのものとなり、 実際にスレーブでビルドが動いても、ファイルはマスタのサーバに出力されるようになっています。

こうみると、ワークスペースはスレーブにも存在するが、ビルドのディレクトリはマスターにしか存在しないってことになりますね。。スレーブで処理されてワークスペースになんかを出力した場合、それはスレーブに出力されるので、スレーブのワークスペースからビルドディレクトリにコピーする必要がありそうです。


この記事は

選択肢 投票
おもしろかった 1  
そうでもない 0  

現在のアクセス:1034


*1 マスターかスレーブか

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2016-04-19 (火) 16:38:53 (1222d)