Jenkins APIを使ってジョブのテンプレートから新しいジョブを作成する


Jenkinsのテンプレートとなるジョブを作っておいて、そこから必要な部分だけを変更して新しいジョブを作成する手順です。
ジョブが1つだけならそれほど手間ではありませんが、似たようなジョブを大量に作る場合に楽をしたくて調べました。

全体的な流れとしては以下の通りです。

* APIトークンを取得しておく
* コピー元ジョブの一覧を取得
* APIトークンを使って、それぞれのジョブ単位でジョブのコピーを実施
* それぞれのコピー後ジョブの単位でconfigを一旦取得
* configを適当に書き換えてジョブ設定を更新

# 事前準備 : APIトークンを調べる

大抵の場合はJenkinsは認証を必要とする場合が多いと思いますが、その場合はこのAPIトークンを調べておく必要があります。

* 画面の右上から「設定」
![スクリーンショット 2015-12-19 0.55.53.png](https://qiita-image-store.s3.amazonaws.com/0/16458/a6aced7d-c66a-dd8a-0746-28e0bcb804a1.png "スクリーンショット 2015-12-19 0.55.53.png")

* APIトークンの部分を控えておきます。
![スクリーンショット 2015-12-19 0.56.08.png](https://qiita-image-store.s3.amazonaws.com/0/16458/091d43e6-8bd1-5e0d-0f9d-03c26ddf8ba9.png "スクリーンショット 2015-12-19 0.56.08.png")

# コピーしたいジョブの一覧を取得

例えば、`Template` という名前のViewからコピー元のジョブ名を取得する場合。
(`localhost:8080`でjenkinsが起動している場合とします)
結果をjson形式で取得し、`jq` によりパースしてジョブ名だけを取得しています。

```sh
curl -s "http://localhost:8080/view/Template/api/json?tree=jobs\[name\]" | ./jq -r ".jobs[] | .name"
testjob
```

ここでは、Template View配下に `testjob` という1つだけが存在しているため、1件しか取得していませんが、複数のジョブをまとめてコピーする場合は同じViewにまとめておくとやりやすいでしょう。

![スクリーンショット 2015-12-19 0.54.01.png](https://qiita-image-store.s3.amazonaws.com/0/16458/600f664b-2f99-a69d-14d5-8b2eb613a29d.png "スクリーンショット 2015-12-19 0.54.01.png")

# ジョブをコピー

先ほどのAPIトークンを利用して、ジョブをコピーします。注意点としては、ここで利用するbasic認証のID/Passwordは、決して **Jenkins画面にログインするときのID/Passwordではない**ということです。

```sh
curl --user 'testuser:c7e7e1260784d54f6f512231941aece5' -H 'Content-type: application/xml' -XPOST "http://localhost:8080/createItem?name=new-testjob&mode=copy&from=testjob"
```

ここでは、先ほど取得した `testjob` というジョブから、新しく `new-testjob` というジョブを作成しています。

ヘッダとして`Content-type: application/xml`を指定しないと、以下のようなエラーが出るケースもあるようですが、条件は不明です。(手元の環境では指定しなくても出なかった)

```
HTTP Status 400 - No Content-Type header set
```

この時点で、ジョブは全く同じ状態でコピーされています。

![スクリーンショット 2015-12-19 1.01.45.png](https://qiita-image-store.s3.amazonaws.com/0/16458/f2254451-b05c-f240-d9da-69e85e0ba777.png "スクリーンショット 2015-12-19 1.01.45.png")

# ジョブの詳細設定を取得

このままでは全く同じジョブが生成されただけなので、必要な書き換えを行います。
一旦、ジョブの設定を手元に取得します。ここでは、`config.xml` という名前で保存しています。

```sh
curl -so config.xml --user 'testuser:c7e7e1260784d54f6f512231941aece5' http://localhost:8080/job/new-testjob/config.xml
```

```xml:config.xml
<?xml version='1.0' encoding='UTF-8'?>
<project>
  <actions/>
  <description></description>
  <keepDependencies>false</keepDependencies>
  <properties/>
  <scm class="hudson.scm.NullSCM"/>
  <canRoam>true</canRoam>
  <disabled>false</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  <triggers/>
  <concurrentBuild>false</concurrentBuild>
  <builders>
    <hudson.tasks.Shell>
      <command>echo &quot;this is testjob&quot;</command>
    </hudson.tasks.Shell>
  </builders>
  <publishers/>
  <buildWrappers/>
</project>
```

`this is testjob` と表示するだけのジョブです。

# ジョブの設定を変更・反映

この部分を書き換えて、新しいジョブになるようにします。

```xml:config.xml(書き換え後)
<?xml version='1.0' encoding='UTF-8'?>
<project>
  <actions/>
  <description></description>
  <keepDependencies>false</keepDependencies>
  <properties/>
  <scm class="hudson.scm.NullSCM"/>
  <canRoam>true</canRoam>
  <disabled>false</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  <triggers/>
  <concurrentBuild>false</concurrentBuild>
  <builders>
    <hudson.tasks.Shell>
      <command>echo &quot;this is new-testjob!&quot;</command>
    </hudson.tasks.Shell>
  </builders>
  <publishers/>
  <buildWrappers/>
</project>
```

`this is new-testjob!` と表示するようにしました。ここだけ変更しています。
実際のケースでは、書き換え対象となるような文字列のパターンを決めて、`sed`などで一気に変更すると良いと思われます。

新しいconfigで反映します。

```sh
curl -XPOST --data-binary @config.xml --user 'testuser:c7e7e1260784d54f6f512231941aece5' http://localhost:8080/job/new-testjob/config.xml
```

`--data-binary`ではなく`-d`とか`--data`オプションでPOSTしてしまうと、改行が崩れてシェルスクリプト部分は動かなくなるので注意してください。

この時点で、以下のようにジョブが変更されています。

![スクリーンショット 2015-12-19 1.05.09.png](https://qiita-image-store.s3.amazonaws.com/0/16458/a75824f8-4c04-a668-c470-4a724ee6894c.png "スクリーンショット 2015-12-19 1.05.09.png")

今回の例では1つのジョブしか変更していないのであまり旨味はないですが、この順序に従って

* 大量のテンプレートジョブを取得
* ジョブ単位に新ジョブ名を指定してコピー生成
* ジョブ単位に設定を書き換えて反映

ということをシェルスクリプトなどで行えばかなり使いやすくなりそうです。

# APIで何ができるのか

今回のケースでは、Jenkins APIのごく一部の機能を使っています。
他にどんな機能があるのかはReferenceのリンクを参照することでも分かりますが、画面上のJenkinsのURLの末尾に`api/`をつけてアクセスするだけでも、大体のことは分かるので便利です。

例えば、ジョブで何ができるのかは
http://localhost:8080/job/new-testjob/api/
で分かりますし、viewで何ができるのかは
http://localhost:8080/view/Template/api/
で分かります。要は、その画面でGUI操作によりできることが、`api`をつければできるようなイメージです。

# Reference

* [Remote access API | Jenkins](https://wiki.jenkins-ci.org/display/JA/Remote+access+API)