Jenkins - z/OS連携: (2) Pipelineの利用

# はじめに
Jenkinsを活用してメインフレーム・アプリケーションの管理をどのように行えるのかを探っていく連載記事です。
今回は、ごくシンプルなPipelineを作成してシェル・コマンドをz/OSに対して実行するところまでやってみます。

## 関連記事
[Jenkins - z/OS連携: (1) 概要](https://qiita.com/tomotagwork/items/d2c90c1f29e07d3b7c19)
[Jenkins - z/OS連携: (2) Pipelineの利用](https://qiita.com/tomotagwork/items/6d0481d58cdd10e44def)
[Jenkins - z/OS連携: (3) Pipeline+Gitの利用](https://qiita.com/tomotagwork/items/16b38599be3f8a134580)
[Jenkins - z/OS連携: (4) Pipeline+Git+Webhookの利用](https://qiita.com/tomotagwork/items/25eb40e56f1a5125e4fc)
[Jenkins - z/OS連携: (5) Pipeline+Git+DBBの利用](https://qiita.com/tomotagwork/items/0b25bbb9a72e37f4d98c)
[Jenkins - z/OS連携: (6) Jenkins Pipeline利用シナリオ](https://qiita.com/tomotagwork/items/9d2b3f697f31f23fd682)

# 環境情報
今回、検証に利用する環境は、IBM Wazi Developer for Red Hat CodeReady WorkspacesというOpenShift上に構成されるz/OS開発環境を利用します。この製品はSandboxと呼ばれるz/OSエミュレーターを中心に各種開発ツールがバンドルされており、DBBなども含まれます。
このWaziのSandbox(z/OSエミュレーター)は、最初からDBBやGitなどが構成された状態のz/OSイメージが提供されているので、今回はそれをそのまま使います。

## z/OS(Sandbox)
z/OS V2.4

## Jenkins Server
Waziの手順に従い、Sandboxと同OpenShift上にJenkins Serverを構成
参考: [Configuring a Jenkins server in OpenShift](https://www.ibm.com/docs/en/wdfrhcw/1.2.0?topic=applications-configuring-jenkins-server-in-openshift)

Jenkins V2.235.5
Git plugin V4.2.2
Git client plugin V3.4.2
SSH Build Agents plugin V1.31.5
SSH Credentials plugin V1.18.1

# シンプルなPipelineの実行
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/279597a1-6dca-b5f6-8e1a-318303ed9eb8.png)

さて、まずはごくシンプルなPipelineを作ってそれをz/OSに対して流してみましょう。

## Jenkins用ユーザー (z/OS)
Jenkinsからz/OSをSlaveとして管理する場合、SSH経由で制御することになるので、SSHで接続できるユーザーを用意しておきます。Jenkinsから実施される処理は基本的にこのユーザーで実行されることになります。

## Jenkins:ノード構成
z/OSをJenkinsの管理対象のノードとして構成します。
ノードを構成すると対象ノードにSSH経由で接続してAgentの構成を行います。AgentはSSH経由でシェルからJavaとして実行されるので、シェルの環境変数やJVMオプションなどを指定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/41bbd196-8b46-78f8-481b-e2b07a1e72c9.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/ee650eff-ab3e-fe8c-4393-c736f233874e.png)


起動時の主要パラメータ補足:

|パラメーター|値|補足|
|---|---|---|
|Javaのパス|/usr/lpp/java/J8.0_64/bin/java||
|JVMオプション|-Xquickstart -Dfile.encoding=UTF-8 -Xnoargsconversion||
|Prefix Start Agent Command|export JAVA_HOME=/usr/lpp/java/J8.0_64 && export IBM_JAVA_ENABLE_ASCII_FILETAG=ON && env &&||
|Suffix Start Agent Command| -text|先頭にブランク|


<details><summary><font color="blue">ノード接続時のログ</font></summary><div>

```
[03/12/21 09:31:46] [SSH] The remote user's environment is:
@="sh"
ERRNO="0"
HOME="/u/JENKIN2"
IFS=" 	
"
LINENO="0"
LOGNAME="JENKIN2"
MAIL="/usr/mail/JENKIN2"
MAILCHECK="600"
OPTIND="1"
PATH="/bin"
PPID="50398323"
PS1="\$ "
PS2="> "
PS3="#? "
PS4="+ "
PWD="/u/JENKIN2"
RANDOM="24367"
SECONDS="0"
SHELL="/bin/sh"
SSH_CLIENT="172.26.1.1 50003 22"
SSH_CONNECTION="172.26.1.1 50003 172.26.1.2 22"
USER="JENKIN2"
[03/12/21 09:31:47] [SSH] Starting sftp client.
[03/12/21 09:31:48] [SSH] Copying latest remoting.jar...
Source agent hash is E5FEC468D6F172BF394E1F2571EA686C. Installed agent hash is E5FEC468D6F172BF394E1F2571EA686C
Verified agent jar. No update is necessary.
Expanded the channel window size to 4MB
[03/12/21 09:31:52] [SSH] Starting agent process: export JAVA_HOME=/usr/lpp/java/J8.0_64 && export IBM_JAVA_ENABLE_ASCII_FILETAG=ON && env &&cd "/u/JENKIN2/agent/wazi-test01" && /usr/lpp/java/J8.0_64/bin/java -Xquickstart -Dfile.encoding=UTF-8 -Xnoargsconversion -jar remoting.jar -workDir /u/JENKIN2/agent/wazi-test01 -jar-cache /u/JENKIN2/agent/wazi-test01/remoting/jarCache -text
MAIL=/usr/mail/JENKIN2
PATH=/bin
SSH_CLIENT=172.26.1.1 50003 22
SHELL=/bin/sh
IBM_JAVA_ENABLE_ASCII_FILETAG=ON
_=/bin/env
LOGNAME=JENKIN2
USER=JENKIN2
HOME=/u/JENKIN2
SSH_CONNECTION=172.26.1.1 50003 172.26.1.2 22
JAVA_HOME=/usr/lpp/java/J8.0_64
Running in text mode
Mar 12, 2021 9:32:47 AM org.jenkinsci.remoting.engine.WorkDirManager initializeWorkDir
INFO: Using /u/JENKIN2/agent/wazi-test01/remoting as a remoting work directory
Mar 12, 2021 9:32:49 AM org.jenkinsci.remoting.engine.WorkDirManager setupLogging
INFO: Both error and output logs will be printed to /u/JENKIN2/agent/wazi-test01/remoting
<===[JENKINS REMOTING CAPACITY]===><===[HUDSON TRANSMISSION BEGINS]===channel started
Remoting version: 4.3
This is a Unix agent
Mar 12, 2021 9:35:43 AM hudson.remoting.UserRequest perform
WARNING: LinkageError while performing UserRequest:jenkins.slaves.StandardOutputSwapper$ChannelSwapper@6884a3c3
java.lang.UnsatisfiedLinkError: Native library (com/sun/jna/z/os-s390x/libjnidispatch.so) not found in resource path ([])
	at com.sun.jna.Native.loadNativeDispatchLibraryFromClasspath(Native.java:1032)
	at com.sun.jna.Native.loadNativeDispatchLibrary(Native.java:988)
	at com.sun.jna.Native.<clinit>(Native.java:195)
	at hudson.util.jna.GNUCLibrary.<clinit>(GNUCLibrary.java:115)
	at jenkins.slaves.StandardOutputSwapper$ChannelSwapper.swap(StandardOutputSwapper.java:60)
	at jenkins.slaves.StandardOutputSwapper$ChannelSwapper.call(StandardOutputSwapper.java:45)
	at jenkins.slaves.StandardOutputSwapper$ChannelSwapper.call(StandardOutputSwapper.java:39)
	at hudson.remoting.UserRequest.perform(UserRequest.java:211)
	at hudson.remoting.UserRequest.perform(UserRequest.java:54)
	at hudson.remoting.Request$2.run(Request.java:369)
	at hudson.remoting.InterceptingExecutorService$1.call(InterceptingExecutorService.java:72)
	at java.util.concurrent.FutureTask.run(FutureTask.java:277)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1160)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.lang.Thread.run(Thread.java:820)

ERROR: Failed to monitor for Clock Difference
java.util.concurrent.TimeoutException
	at hudson.remoting.Request$1.get(Request.java:316)
	at hudson.remoting.Request$1.get(Request.java:240)
	at hudson.remoting.FutureAdapter.get(FutureAdapter.java:59)
	at hudson.node_monitors.AbstractAsyncNodeMonitorDescriptor.monitorDetailed(AbstractAsyncNodeMonitorDescriptor.java:114)
	at hudson.node_monitors.AbstractAsyncNodeMonitorDescriptor.monitor(AbstractAsyncNodeMonitorDescriptor.java:78)
	at hudson.node_monitors.AbstractNodeMonitorDescriptor$Record.run(AbstractNodeMonitorDescriptor.java:306)
ERROR: Failed to monitor for Architecture
java.util.concurrent.TimeoutException
	at hudson.remoting.Request$1.get(Request.java:316)
	at hudson.remoting.Request$1.get(Request.java:240)
	at hudson.remoting.FutureAdapter.get(FutureAdapter.java:59)
	at hudson.node_monitors.AbstractAsyncNodeMonitorDescriptor.monitorDetailed(AbstractAsyncNodeMonitorDescriptor.java:114)
	at hudson.node_monitors.AbstractAsyncNodeMonitorDescriptor.monitor(AbstractAsyncNodeMonitorDescriptor.java:78)
	at hudson.node_monitors.AbstractNodeMonitorDescriptor$Record.run(AbstractNodeMonitorDescriptor.java:306)
ERROR: Failed to monitor for Free Disk Space
java.util.concurrent.TimeoutException
	at hudson.remoting.Request$1.get(Request.java:316)
	at hudson.remoting.Request$1.get(Request.java:240)
	at hudson.remoting.FutureAdapter.get(FutureAdapter.java:59)
	at hudson.node_monitors.AbstractAsyncNodeMonitorDescriptor.monitorDetailed(AbstractAsyncNodeMonitorDescriptor.java:114)
	at hudson.node_monitors.AbstractAsyncNodeMonitorDescriptor.monitor(AbstractAsyncNodeMonitorDescriptor.java:78)
	at hudson.node_monitors.AbstractNodeMonitorDescriptor$Record.run(AbstractNodeMonitorDescriptor.java:306)
ERROR: Failed to monitor for Response Time
java.util.concurrent.TimeoutException
	at hudson.remoting.Request$1.get(Request.java:316)
	at hudson.remoting.Request$1.get(Request.java:240)
	at hudson.remoting.FutureAdapter.get(FutureAdapter.java:59)
	at hudson.node_monitors.AbstractAsyncNodeMonitorDescriptor.monitorDetailed(AbstractAsyncNodeMonitorDescriptor.java:114)
	at hudson.node_monitors.ResponseTimeMonitor$1.monitor(ResponseTimeMonitor.java:57)
	at hudson.node_monitors.AbstractNodeMonitorDescriptor$Record.run(AbstractNodeMonitorDescriptor.java:306)
ERROR: Failed to monitor for Free Temp Space
java.util.concurrent.TimeoutException
	at hudson.remoting.Request$1.get(Request.java:316)
	at hudson.remoting.Request$1.get(Request.java:240)
	at hudson.remoting.FutureAdapter.get(FutureAdapter.java:59)
	at hudson.node_monitors.AbstractAsyncNodeMonitorDescriptor.monitorDetailed(AbstractAsyncNodeMonitorDescriptor.java:114)
	at hudson.node_monitors.AbstractAsyncNodeMonitorDescriptor.monitor(AbstractAsyncNodeMonitorDescriptor.java:78)
	at hudson.node_monitors.AbstractNodeMonitorDescriptor$Record.run(AbstractNodeMonitorDescriptor.java:306)
ERROR: Failed to monitor for Free Swap Space
java.util.concurrent.TimeoutException
	at hudson.remoting.Request$1.get(Request.java:316)
	at hudson.remoting.Request$1.get(Request.java:240)
	at hudson.remoting.FutureAdapter.get(FutureAdapter.java:59)
	at hudson.node_monitors.AbstractAsyncNodeMonitorDescriptor.monitorDetailed(AbstractAsyncNodeMonitorDescriptor.java:114)
	at hudson.node_monitors.AbstractAsyncNodeMonitorDescriptor.monitor(AbstractAsyncNodeMonitorDescriptor.java:78)
	at hudson.node_monitors.AbstractNodeMonitorDescriptor$Record.run(AbstractNodeMonitorDescriptor.java:306)
Agent successfully connected and online
The Agent is connected, disconnect it before to try to connect it again.
```

</div></details>

## Pipelineの作成
新規ジョブ作成を選択します。
名前を指定して、"パイプライン"を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/11b0ab7a-0a76-863b-a90e-e089b510bdef.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/f27f6523-e98b-ac5e-eb00-0f1e2b71729e.png)

Pipeline Scriptとして以下を記載します。

```groovy:pipeline_script
pipeline {
   agent {label 'wazi-test01'}

   stages {
      stage('Hello') {
         steps {
            echo 'Hello World'
         }
      }
      stage('Hello2'){
         steps {
             sh """
               echo 'Hello World2'
               hostname
               pwd
             """
         }
      }
      stage('Hello3'){
         steps {
            sh """
               echo 'Hello World3'
               ls -la
            """
         }
      }
   }
}
```

先頭の`agent {label 'wazi-test01'}`で、実行するノードのラベルを指定しています。
処理内容としては、3つのステージを定義し、それぞれ単純なシェル・コマンドを実行しているだけです。

## Pipeline実行

Pipelineを開いて左側のメニューからビルド実行を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/b85be2f1-2c01-f238-5c31-cbfaa7aa323b.png)

ログを確認

<details><summary><font color="blue">パイプライン実行時のログ</font></summary><div>

```
Started by user IAM#xxxxx
Running in Durability level: MAX_SURVIVABILITY
[Pipeline] Start of Pipeline
[Pipeline] node
Running on wazi-test01 in /u/JENKIN2/agent/wazi-test01/workspace/Tag_pipeline_hello
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Hello)
[Pipeline] echo
Hello World
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Hello2)
[Pipeline] sh
+ echo Hello World2 
Hello World2
+ hostname 
S0W1.DAL-EBIS.IHOST.COM
+ pwd 
/u/JENKIN2/agent/wazi-test01/workspace/Tag_pipeline_hello
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Hello3)
[Pipeline] sh
+ echo Hello World3 
Hello World3
+ ls -la 
total 16
drwxr-xr-x   2 JENKIN2  SYS1           0 Mar 12 09:53 .
drwxr-xr-x   4 JENKIN2  SYS1        8192 Mar 12 09:53 ..
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS
```

</div></details>

z/OSのUSS上で指定したシェルのコマンドが実行されたのが確認できました！

ちなみに、Piplineで実行するシェル・コマンドにsleepを入れてz/OS側のファイル構造を覗いてみると、こんな感じで実行されていました。

```
TAGUCHI:/u/JENKIN2/agent/wazi-test01/workspace: >ls -laR

.:
total 48
drwxr-xr-x   4 JENKIN2  SYS1        8192 Mar 12 18:53 .
drwx------   5 JENKIN2  SYS1        8192 Mar 12 18:53 ..
drwxr-xr-x   2 JENKIN2  SYS1           0 Mar 12 18:53 Tag_pipeline_hello
drwxr-xr-x   3 JENKIN2  SYS1        8192 Mar 13 10:46 Tag_pipeline_hello@tmp

./Tag_pipeline_hello:
total 16
drwxr-xr-x   2 JENKIN2  SYS1           0 Mar 12 18:53 .
drwxr-xr-x   4 JENKIN2  SYS1        8192 Mar 12 18:53 ..

./Tag_pipeline_hello@tmp:
total 48
drwxr-xr-x   3 JENKIN2  SYS1        8192 Mar 13 10:46 .
drwxr-xr-x   4 JENKIN2  SYS1        8192 Mar 12 18:53 ..
drwxr-xr-x   2 JENKIN2  SYS1        8192 Mar 13 10:46 durable-f42e8941

./Tag_pipeline_hello@tmp/durable-f42e8941:
total 66
drwxr-xr-x   2 JENKIN2  SYS1        8192 Mar 13 10:46 .
drwxr-xr-x   3 JENKIN2  SYS1        8192 Mar 13 10:46 ..
-rw-r--r--   1 JENKIN2  SYS1         135 Mar 13 10:46 jenkins-log.txt
-rw-r--r--   1 JENKIN2  SYS1           2 Mar 13 10:46 jenkins-result.txt
-rw-r--r--   1 JENKIN2  SYS1          92 Mar 13 10:46 script.sh
TAGUCHI:/u/JENKIN2/agent/wazi-test01/workspace: >cat *tmp/d*/script.sh

               echo 'Hello World3'
               sleep 20
               ls -la
```

workspace以下にtempのディレクトリが作成されて、pipelineのstepで指定されたシェル・コマンドがscript.shというファイルとして作成されていました。
Pipeline実行後はこれらのファイルは削除されています。

# まとめ
今回の試した内容は以下のイメージです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/73da8b5d-ed23-ab0e-a3ab-e5bba07a34b7.png)

シンプルな例なので非常に分かりやすいですね。