// 下階層用テンプレート
#topicpath
----
//ここにコンテンツを記述します。
#contents

** Jenkinsの抽象化されたjava.io.Fileである FilePath [#o7ffc559]
ビルドのジョブが動くノード((マスターかスレーブか))によって、ワークスペースのパスは異なっていたりします。なので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
となります。これはマスタで動いてしまいましたが、一行目はワークスペースなので場合によっては別ノードのパスになります。逆に二行目はビルドパスなので必ずマスターサーバのパスになります。


**ファイル出力すると [#x0297ccb]
プラグインからファイルを出力したいときがありますが、たとえば
 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が指している場所への参照になっているわけですね。。


** サンプルコード [#h8a3f9e0]
サンプルコード:
 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の参照を返すメソッドについてはスレーブで実行してもファイルパスはマスターのものとなり、
実際にスレーブでビルドが動いても、ファイルはマスタのサーバに出力されるようになっています。


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

----
この記事は
#vote(おもしろかった,そうでもない)
#vote(おもしろかった[1],そうでもない[0])


#comment
#topicpath


SIZE(10){現在のアクセス:&counter;}

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS