Dockerで始めるStackstorm再入門1/3(環境構築からOrquestaで書いたWorkflowの結果をslackに通知する)

これは [エーピーコミュニケーションズ Advent Calendar 2019](https://qiita.com/advent-calendar/2019/ap-com) の17日目の記事です。


- [Dockerで始めるStackstorm再入門1/3(環境構築からOrquestaで書いたWorkflowの結果をslackに通知する)](https://qiita.com/gkzz/items/4329f0846c873dbce9cf)
- [Dockerで始めるStackstorm再入門2/3(条件分岐させるWorkflowと定期実行させるRuleを書く)](https://qiita.com/gkzz/items/85f119fc7771d5de582d)
- [Dockerで始めるStackstorm再入門3/3(pythonスクリプトとmockを使ったテストコードを書く)](https://qiita.com/gkzz/items/286f7b5ee3fd6674cb11)
- 番外編
  - [Dockerコンテナ上に立てたStacksormのWorkflowからAnsibleのPlaybookの結果をslackに通知する[junos_commandモジュール活用]](https://qiita.com/gkzz/items/ad52628e5f89d2ad6541)

## 0. そもそもStackstormとは

Stackstorm
みなさん、ご存知でしょうか？

Stackstormは、`イベント駆動型`でひとつひとつの処理を下の図のようにWorkflowとして繋げながら、定形処理の自動化を進めることができるOSSです。
![Screenshot from 2020-01-18 15-46-41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/341653/165c2752-2109-0e91-d957-777d0e9508e0.png)

出所：[StackstormというIFTTT的なツールをDockerコンテナに乗せた小話]()



Stackstormではこのひとつひとつの処理をActionと呼ぶので、WorkflowとはActionを組み合わせたものともいえるでしょう。

もし、IFTTTというサービスをご存知でしたら、IFTTTでやっていることができると考えてください。
実際、Stackstormはその特性から`IFTTT for Ops`なんて呼ばれることもあります。



### Stackstorm v.s. Jenkins
Stackstormは類似OSSとしてJenkinsと比較されることがありますが、
学習コストを抑えて手っ取り早く自動化を推し進めるならJenkins、`切り戻しや処理の並列処理などワークフローを柔軟に定義`したい場合はStackstormを選ぶのがいいのかなと思います。

外部サービスを活用することはJenkinsと同様に簡単です。
Stackstormではpackと呼ばれる拡張プラグインが提供されています。




## 1. Orquestaという新しいワークフローエンジン
さて、そんなStackstormに新しいワークフローエンジンの`Orquesta`が発表されました。
公式のドキュメントによれば、このように謳われています。

- Orquestaは`Stackstorm自身が開発`したネイティブなワークフローエンジン
- 目標はMistralやActionChainを代替できるところまでもっていくこと
- Mistralはopenstackが開発

>Orquesta is a workflow engine designed specifically to run natively in StackStorm. The goal is to replace ActionChain and Mistral in the future. 

参考: 
[Orquesta &mdash; StackStorm 3.1.0 documentation]
(https://docs.stackstorm.com/orquesta/index.html)



**ただ残念なことにサンプルコードが充実していません！！！！！！！！泣**

そこで、今回複数サンプルコードを作ってみました。
(めちゃくちゃがんばったので、みなさん奮って記事にいいね！をお願いします！！)

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">st2からの出力結果を整形した。 <a href="https://t.co/BtQNnInIzf">pic.twitter.com/BtQNnInIzf</a></p>&mdash; gkz (@gkzvoice) <a href="https://twitter.com/gkzvoice/status/1206171881792913408?ref_src=twsrc%5Etfw">December 15, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


これらのサンプルコードはDocker上で動くStackstormを使うので、どなたでもStackstormの雰囲気を感じることができるはずです。
ぜひチャレンジしてみてください。

本記事では[こちらのリポジトリ](https://github.com/gkzz/st2-docker-gkz/tree/tutorial-install-pack)をベースにお話するので、併せてご参照ください。

2020/02/05更新
masterブランチにマージしたので、tutorial-install-packにcheck outしなくて大丈夫です。
[gkzz/st2-docker-gkz](https://github.com/gkzz/st2-docker-gkz)


前置きが長くなりましたが、本記事の目次です。

## 2. 目次
- 3. 構成図とバージョン情報
- 4. Dockerホスト側で準備すること
- 5. Dockerコンテナ側で準備すること
- 6. Actionを使ってディストリビューションを確かめる
- 7. ActionとWorkflowを使ってディストリビューションを確かめてslackに通知する
- 8. ファイルの編集方法やトラブルシュートと次回予告



## 3. 構成図とバージョン情報
### 構成図
![Screenshot from 2019-12-16 22-01-15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/341653/47b384ee-d9c0-3b27-6951-8237af7f3deb.png)



※1
Stackstormコンテナ内から同コンテナ外にあるアプリ(flask/nginx)コンテナに接続するために、
`ホストのDockerデーモンのソケットファイル(var/run/docker.sock)`をマウントさせています。

```
ubuntu@st2-pub-1a ~/st2-docker-gkz (tutorial-install-pack) $ cat docker-compose-dev.yml | grep -n -E "/var/run/docker|app"
55:      - /var/run/docker.sock:/var/run/docker.sock
56:      - /app:/usr/src/app
```

※2
Gtihubからリポジトリをcloneする際、ssh鍵やパスワードを使わずにおこなうべく、`Personal access tokens`を使いました。
トークンの取得方法は下記の記事をご参照ください。
私はrepoをスコープとしてトークンを取得しました。

```
ubuntu@st2-pub-1a ~/st2-docker-gkz (tutorial-install-pack) $ cat images/stackstorm/opt/reload.d/clone_repo.sh | grep -n -E "clone|x-oauth-basic@github.com/"
6:    git clone \
7:    "https://${GITHUB_SECRET_TOKEN}:x-oauth-basic@github.com/${GITHUB_ACCT}/${SERVICE_DIR}.git"
```

参考
[Creating a personal access token for the command line - GitHub Help](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line)


GITHUB_SECRET_TOKEN、ACCT、SERVICE_DIRはそれぞれ環境変数として扱い、docker-compose-dev.ymlでコンテナ側に渡しています。

```
$ cat docker-compose-dev.yml | grep -n -E "GITHUB_SECRET_TOKEN|ACCT|SERVICE_DIR"
26:      - GITHUB_ACCT                   #https://github.com/$YOURNAMEの$YOURNAME
27:      - GITHUB_SECRET_TOKEN 　　　　　　#上述したPersonal access tokens
28:      - SERVICE_DIR   　　　　　　　　　 #flask-docker
```

### バージョン情報
- AWS (EC2, VPC, EIP, etc)
  - `Ubuntu 18.04.3 LTS`
  - t2.large
  - パブリックサブネット1つのみ 
  - パブリックIP有効化(Auto-assign Public IP Enable)
  - ストレージはGeneral Purpose SSD (gp2)、8size (t2.largeを選択した場合の初期値)
  - セキュリティグループはsshはローカルのみ接続許可、HTTPとHTTPSはフルオープン
  　(HTTPはアプリコンテナ、HTTPSはStackstormをwebからそれぞれ閲覧できるように許可)

![sg_st2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/341653/f5a21ab3-8b16-ba30-36b1-b6ba3562efd7.jpeg)



- Stackstormらバージョン
  - `Stackstorm: 3.1.0, on Python 2.7.6`  #Stackstormでorquestaを使うためにはバージョンが3.0以上である必要があります。
  - RabbitMQ: 3.6
  - MongoDB: 3.4
  - Redis: 4.0
  - Postgresql: 9.6
  - Docker version 19.03.5, build 633a0ea838
  - docker-compose version 1.24.1, build 4667896b
- Stackstormで管理する対象のDockerコンテナで使っているFlaskらのバージョン
  - python:3.7.2-stretch  
  - nginx:1.17.6   

## 4. Dockerホスト側で準備すること
- 4-1. EC2インスタンス立ち上げ
- 4-2. 今回扱うst2-dockerのリポジトリをクローン
- 4-3. 必要な環境変数を~/.bashrcらに記載
- 4-4. 適宜インストール


### 4-1. EC2インスタンス立ち上げ
基本的には上述しているEC2のスペックを参考に設定をおこなってください。
とくにディストリビューションは`ubuntu18.04`でしか動作確認をおこなっていないので、ご注意ください。 

ところで、起動するインスタンスを設定する際にホスト名をワンライナーで設定することもできます。

設定する場所は**Step 3: Configure Instance Details**と書かれたところです。

![Screenshot from 2019-11-29 19-39-26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/341653/9c2841ac-f5aa-173b-d2b1-d5e93b083bd2.png)

ホスト名をst2-pub-1aとする場合はこのように書きます。

```sh
#!/bin/bash

EC2_HOSTNAME="st2-pub-1a" \
&& sudo sed \
-i -e 's|127.0.0.1 localhost|127.0.0.1 localhost '"${EC2_HOSTNAME}"'|' \
/etc/hosts \
&& sudo hostnamectl set-hostname ${EC2_HOSTNAME}
```

あとはインスタンスを起動させてください。

### 4-2. 今回扱うst2-dockerのリポジトリをクローン
ブランチですが、2091年12月16日現在、git checkout `tutorial-install-pack`に移動して以下のチュートリアルを進めてください。
変更があれば、本記事に追記します。

```
ubuntu@st2-pub-1a ~ $ git clone https://github.com/gkzz/st2-docker-gkz.git
ubuntu@st2-pub-1a ~ $ cd st2-docker-gkz && git checkout tutorial-install-pack
```

### 4-3. ~/.basrhcなどに環境変数を記入
(source ~/.bashrcなどで読み込みも。)

```
ubuntu@st2-pub-1a ~/st2-docker-gkz (tutorial-install-pack) $ tail ~/.bashrc
export HOST_IP='xxx.xxx.xxx.xxx'
export SLACKBOT_NAME='$YOURBOTNAME'
#xoxb-your-slack-token
export SLACKBOT_TOKEN='$YOURBOTTOKEN'
export INCOMING_WEBHOOK_URL='https://hooks.slack.com/services/xxx/yyy/zzz'
export DST_CHANNEL='$CHANNEL'
#https://github.com/$YOURNAME
export GITHUB_ACCT='$YOURNAME'
export GITHUB_SECRET_TOKEN='xxxxxxxxxxxxxxxxxxxx'
export SERVICE_DIR='flask-docker'
```

### 4-4. 適宜インストール
make、docker、docker-composeがそれぞれ入っているか確かめたい場合は、以下のようにコマンドを叩いてください。

make

```
ubuntu@st2-pub-1b-1a ~/st2-docker-gkz (tutorial-install-pack) $ make --version
GNU Make 4.1
Built for x86_64-pc-linux-gnu
Copyright (C) 1988-2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
```

docker

```
ubuntu@st2-pub-1a ~/st2-docker-gkz (tutorial-install-pack) $ docker --version
Docker version 19.03.5, build 633a0ea838
```

docker-compose

```
ubuntu@st2-pub-1a ~/st2-docker-gkz (tutorial-install-pack) $ docker-compose --version
docker-compose version 1.24.1, build 4667896b
```

リポジトリをクローンした方は`./sh/install.sh`を実行すれば、同スクリプトがインストールされていないことを確認してから、インストールコマンドが実行されます。

```
ubuntu@st2-pub-1a ~/st2-docker-gkz (tutorial-install-pack) $ source sh/install.sh 
```

いよいよ、stackstormのDockerコンテナを立ち上げるのですが、st2-dockerでは`stackstormのユーザー名やパスワードなどセキュアな情報は/conf/*.envから読み込んでいます。`

そのため、make envを実行して/conf/*.envを作りましょう。
こちらのユーザー名とパスワードはstackstormのwebポータル(https://${HOST_IP})にログインする際に使います。

![Screenshot from 2019-12-16 09-24-52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/341653/e02099c1-7717-b3ed-f33f-bc420215c115.png)


ログインするとこのようにGUIからでも登録されたpackなどが確認できます。
![Screenshot from 2019-12-16 09-26-32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/341653/df3be085-7e4f-1ea4-b20d-5a3e076ffdc6.png)




```
ubuntu@st2-pub-1a ~/st2-docker-gkz (tutorial-install-pack) $ make env
bin/write-env.sh conf
```

おまたせしました！
それでは、docker-compose upでstackstormコンテナを立ち上げましょう。

```
ubuntu@st2-pub-1a ~/st2-docker-gkz (tutorial-install-pack) $ docker-compose -f docker-compose-dev.yml up -d --build
```

## 5. Dockerコンテナ側で準備すること

コンテナ側で行うことは、必要なpackなどをインストールために`st2-docker/opt/reload.sh`を実行するだけです。

```/st2-docker/opt/reload.sh
#!/bin/bash

# login
st2 login $ST2_USER -p $ST2_PASSWORD

# publish ST2_API_KEY
export ST2_API_KEY=$(st2 apikey create -k -m '{"used_by": "my integration"}')

# install packs, clone my repo
FILES=/st2-docker/opt/reload.d/*
for f in $FILES; 
do
  echo "Execute $f"
  bash "$f" -H
done

# change owner, mode of mydemo_pack
chown -R root:st2packs /opt/stackstorm/packs/mydemo_pack/ \
&& chmod -R +x /opt/stackstorm/packs/mydemo_pack/

# create, and reload my specified actions one by one
# if you don't need to, just run the following commnad
st2ctl reload --register-all
#actions_dir="/opt/stackstorm/packs/mydemo_pack/actions" \
#&& files=$(ls $actions_dir | grep -E "*.yaml")
#for f in $files;
#do
#  st2 action create /opt/stackstorm/packs/mydemo_pack/actions/$f
#done
#
#pack_name="mydemo_pack" \
#&& st2 run packs.setup_virtualenv packs=$pack_name

# run actions
st2 run ansible.playbook \
inventory_file=/opt/stackstorm/packs/ansible/inventory/hosts \
playbook=/opt/stackstorm/packs/ansible/playbook/ping.yaml

st2 run slack.post_message \
message="Hello World! $HOSTNAME by $SLACKBOT_NAME"

#st2 run mydemo_pack.poll-repo

#st2 run mydemo_pack.my-first-wf爆速

```


```
ubuntu@st2-pub-1a ~/st2-docker-gkz (tutorial-install-pack) $ docker-compose exec \ 
> stackstorm bash
root@$HOSTNAME:/# . st2-docker/opt/reload.sh
```

st2-docker/opt/reload.shでは`同シェルスクリプトと同じパスにあるreload.dディレクトリ内にあるシェルスクリプトをひとつずつ実行`しています。
ご自身で追加したいpackや変更したい内容があればreload.dディレクトリ内にシェルスクリプトを追加するか、既存のシェルスクリプトを修正すればOKです。



```
root@$HOSTNAME:/# tree /st2-docker/opt/
/st2-docker/opt/
├── reload.d
│   ├── add-ansible-pack.sh             # ansible packを追加
│   ├── add-slack-pack.sh               # slack packを追加
│   ├── clone_repo.sh                   # stackstormで管理する対象アプリのリポジトリがなければクローン
|                                       # アプリ(flask/nginx)コンテナを立ち上げる
│   └── create-pack-mydemo.sh           # mydemo packを追加
├── reload.sh                           # reload.d内にあるシェルスクリプトの実行ファイル
└── requirements.txt.dev

1 directory, 6 files
```

ansible,slack, mydemo packの3つのpackが入っているか確認しましょう。

```
root@$HOSTNAME:/# st2 pack list | grep -E "ansible|slack|mydemo_pack"
| ansible     | ansible     | ansible          | 0.5.9   | StackStorm, Inc. |
| mydemo_pack | mydemo_pack | st2 mydemo pack  | 0.0.1   | Gakuji Tamaki    |
| slack       | slack       | Slack Chat       | 0.12.8  | StackStorm, Inc. |
root@db8b2e092ffd:/# 
```

用意したslackのチャンネルをみてみると、Botがコンテナの$HOSTNAMEを投稿していることが確認できます。

![Screenshot from 2019-12-07 17-56-12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/341653/978b135f-733f-71a0-7bfe-e1bcf1668dd8.png)

![Screenshot from 2019-12-07 17-59-32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/341653/22a19893-1938-423e-9a63-6938226415e2.png)

やっと下準備が終わりました。
Stackstormはユーザーがカスタマイズできるがゆえに、下ごしらえが大変です。

なので、今回僕が用意したリポジトリのように、Dockerfileや環境変数、シェルスクリプトなどを使って少しでもインフラ構成のコード化を推し進めることが、Stackstormの運用が多少なりともラクになるのではないかと考えます。

(うれしいことにStackstormのミートアップが近々行われるということなので、そこでみなさんの運用術をお聞きして、また記事に起こせれば！)

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ドンピシャでst2のミートアップがきた。 <a href="https://t.co/PTeevL0IjH">https://t.co/PTeevL0IjH</a></p>&mdash; gkz (@gkzvoice) <a href="https://twitter.com/gkzvoice/status/1206551685864153088?ref_src=twsrc%5Etfw">December 16, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


## 6. Actionを使ってディストリビューションを確かめる
ここからActionやWorkflowを使っていくので、まずどんなものがあるか確認しましょう。
`st2 action list -p=$PACK`と叩いてみてください。

```
root@$HOSTNAME:/# st2 action list -p=mydemo_pack
+-------------------------------------+-------------+-------------------------------------+
| ref                                 | pack        | description                         |
+-------------------------------------+-------------+-------------------------------------+
| mydemo_pack.get_distro              | mydemo_pack | get distro  #6.                     |
| mydemo_pack.git_status              | mydemo_pack | git status                          |
| mydemo_pack.git_status_python       | mydemo_pack | git status with python              |
| mydemo_pack.my-first-wf             | mydemo_pack | return code depends on the result   |
| mydemo_pack.orquesta-ping-with-     | mydemo_pack | A workflow demonstrating with       |
| items                               |             | items.                              |
| mydemo_pack.playbook-demo           | mydemo_pack | Playbook Demo                       |
| mydemo_pack.poll-repo               | mydemo_pack | poll repo                           |
| mydemo_pack.poll-repo-python        | mydemo_pack | poll repo with python               |
| mydemo_pack.rebuild_app             | mydemo_pack | Rebuild App Container               |
+-------------------------------------+-------------+-------------------------------------+
root@$HOSTNAME:/# 

```
続いて引数(parameters)として何を渡せばよいか確認するために、`st2 action get $PACK.$ACTION`と叩いて、parametersの横に書かれたnameやdefault、descriptionを参照します。
（結論をいうと、centosやubuntuなどディストリビューションをdistro_name=centosとして渡してほしいです。）

```
root@$HOSTNAME:/# st2 action get mydemo_pack.get_distro
+---------------+-------------------------------+
| Property      | Value                         |
+---------------+-------------------------------+
| id            | 5df796581b25dd0132571c0f      |
| uid           | action:mydemo_pack:get_distro |
| ref           | mydemo_pack.get_distro        |
| pack          | mydemo_pack                   |
| name          | get_distro                    |
| description   | get distro                    |
| enabled       | True                          |
| entry_point   | scripts/get_distro.sh         |
| runner_type   | local-shell-script            |
| parameters    | {                             |
|               |     "distro_name": {          |
|               |         "position": 0,        |
|               |         "required": true,     |
|               |         "type": "string"      |
|               |         "default": "centos"   |
|               |     }                         |
|               | }                             |
| metadata_file | actions/get_distro.yaml       |
| notify        |                               |
| output_schema |                               |
| tags          |                               |
+---------------+-------------------------------+
root@$HOSTNAME:/#

```

それではActionを実行してみましょう。

```
root@$HOSTNAME:/# st2 run mydemo_pack.get_distro distro_name=centos
.
id: 5df797271b25dd0132571c22
status: failed
parameters: 
  distro_name: centos
result: 
  failed: true
  return_code: 1
  stderr: ''
  stdout: ''
  succeeded: false
root@$HOSTNAME:/# 

```
succeededがfalse、あるいはfailedがtrueであればそのActionは失敗したことになります。
続いて`distro_nameにubuntu`を渡して再度実行してみましょう。

```
root@$HOSTNAME:/# st2 run mydemo_pack.get_distro distro_name=ubuntu
.
id: 5df797141b25dd0132571c1f
status: succeeded
parameters: 
  distro_name: ubuntu
result: 
  failed: false
  return_code: 0
  stderr: ''
  stdout: ''
  succeeded: true
root@$HOSTNAME:/# 
```

今度は上手く行きました。
このようにActionの実行コマンドは `st2 run $PACK.$ACTION`となっています。
Actionの結果を確認する場合のコマンドは`st2 execution get $ID`です。
$IDは、Actionごとにユニークに割り当てられており、IDが分からない場合は、`st2 execution list`で確認できます。

```
root@$HOSTNAME:/# st2 execution list
+--------------------------+------------+--------------+-------------------------+-----------------+---------------+
| id                       | action.ref | context.user | status                  | start_timestamp | end_timestamp |
+--------------------------+------------+--------------+-------------------------+-----------------+---------------+
| 5df84aa3b530ad0122f3ac93 | packs.setu | st2admin     | succeeded (9s elapsed)  | Tue, 17 Dec     | Tue, 17 Dec   |
|                          | p_virtuale |              |                         | 2019 03:25:23   | 2019 03:25:32 |
|                          | nv         |              |                         | UTC             | UTC           |
| 5df84b0cb530ad0122f3ac97 | packs.inst | st2admin     | succeeded (47s elapsed) | Tue, 17 Dec     | Tue, 17 Dec   |
|                          | all        |              |                         | 2019 03:27:08   | 2019 03:27:55 |
|                          |            |              |                         | UTC             | UTC           |
| 5df84b3fb530ad0122f3aca3 | packs.inst | st2admin     | succeeded (24s elapsed) | Tue, 17 Dec     | Tue, 17 Dec   |
|                          | all        |              |                         | 2019 03:27:59   | 2019 03:28:23 |
|                          |            |              |                         | UTC             | UTC           |
| 5df84b5cb530ad0122f3ad3b | packs.inst | st2admin     | succeeded (17s elapsed) | Tue, 17 Dec     | Tue, 17 Dec   |
|                          | all        |              |                         | 2019 03:28:28   | 2019 03:28:45 |
|                          |            |              |                         | UTC             | UTC           |
| 5df84b7eb530ad0122f3ad48 | ansible.pl | st2admin     | succeeded (20s elapsed) | Tue, 17 Dec     | Tue, 17 Dec   |
|                          | aybook     |              |                         | 2019 03:29:02   | 2019 03:29:22 |
|                          |            |              |                         | UTC             | UTC           |
| 5df84b95b530ad0122f3ad4b | slack.post | st2admin     | succeeded (1s elapsed)  | Tue, 17 Dec     | Tue, 17 Dec   |
|                          | _message   |              |                         | 2019 03:29:25   | 2019 03:29:26 |
|                          |            |              |                         | UTC             | UTC           |
| 5df84d05b530ad0122f3ad4e | mydemo_pac | st2admin     | failed (0s elapsed)     | Tue, 17 Dec     | Tue, 17 Dec   |
|                          | k.get_dist |              |                         | 2019 03:35:33   | 2019 03:35:33 |
|                          | ro         |              |                         | UTC             | UTC           |
| 5df84d10b530ad0122f3ad51 | mydemo_pac | st2admin     | succeeded (0s elapsed)  | Tue, 17 Dec     | Tue, 17 Dec   |
|                          | k.get_dist |              |                         | 2019 03:35:44   | 2019 03:35:44 |
|                          | ro         |              |                         | UTC             | UTC           |
+--------------------------+------------+--------------+-------------------------+-----------------+---------------+
root@$HOSTNAME:/# st2 execution get 5df84d05b530ad0122f3ad4e
id: 5df84d05b530ad0122f3ad4e
status: failed (0s elapsed)
parameters: 
  distro_name: centos
result: 
  failed: true
  return_code: 1
  stderr: ''
  stdout: ''
  succeeded: false
root@$HOSTNAME:/# st2 execution get 5df84d10b530ad0122f3ad51
id: 5df84d10b530ad0122f3ad51
status: succeeded (0s elapsed)
parameters: 
  distro_name: ubuntu
result: 
  failed: false
  return_code: 0
  stderr: ''
  stdout: ''
  succeeded: true
root@$HOSTNAME:/# 
```

このように都度実行結果を確認しながらActionを実行してもよいのですが、Stackstormでは複数のActionを出力結果や引数に応じて柔軟に繋げることができるWorkflowという機能も提供されているので、Workflowを使ってみましょう。


## 7. ActionとWorkflowを使ってディストリビューションを確かめてslackに通知する
workflowで行っていることをフロー図に起こしました。
![Screenshot from 2019-12-21 14-20-46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/341653/eb8814af-503d-843f-b5e4-a1ce1779ee01.png)
出所：近日参加予定のLT資料より。(完成次第、リンクを貼ります。)

それでは、下記のとおりworkflowを実行してみましょう。

```
root@$HOSTNAME:/# st2 run mydemo_pack.my-first-wf
....
id: 5df8bec8b530ad0122f3ad54
action.ref: mydemo_pack.my-first-wf
parameters: None
status: succeeded
start_timestamp: Tue, 17 Dec 2019 11:40:56 UTC
end_timestamp: Tue, 17 Dec 2019 11:41:03 UTC
result: 
  output:
    failed: false
+--------------------------+------------------------+----------+------------+-----------------+
| id                       | status                 | task     | action     | start_timestamp |
+--------------------------+------------------------+----------+------------+-----------------+
| 5df8bec8b530ad0035fd7575 | succeeded (1s elapsed) | init     | core.noop  | Tue, 17 Dec     |
|                          |                        |          |            | 2019 11:40:56   |
|                          |                        |          |            | UTC             |
| 5df8bec9b530ad0035fd7578 | failed (1s elapsed)    | main     | mydemo_pac | Tue, 17 Dec     |
|                          |                        |          | k.get_dist | 2019 11:40:57   |
|                          |                        |          | ro         | UTC             |
| 5df8becbb530ad0035fd757b | succeeded (0s elapsed) | main     | mydemo_pac | Tue, 17 Dec     |
|                          |                        |          | k.get_dist | 2019 11:40:59   |
|                          |                        |          | ro         | UTC             |
| 5df8beccb530ad0035fd757e | succeeded (0s elapsed) | post_msg | slack.post | Tue, 17 Dec     |
|                          |                        |          | _message   | 2019 11:41:00   |
|                          |                        |          |            | UTC             |
| 5df8becdb530ad0035fd7581 | succeeded (1s elapsed) | last     | core.noop  | Tue, 17 Dec     |
|                          |                        |          |            | 2019 11:41:01   |
|                          |                        |          |            | UTC             |
+--------------------------+------------------------+----------+------------+-----------------+
root@$HOSTNAME:/# 

```

![Screenshot from 2019-12-17 23-23-54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/341653/c294d12f-a044-5d99-f5cf-151e57e5e90c.png)


Actionとworkflowに関する解説は次回に譲りますが、ポイントはfailedというフラグを用意している点です。

workflowはひとつでもActionがfailedとなったとき、workflow(Actio全体)の結果としてもfailedとなってしまいます。
workflowのなかでActionがfailedのルートに入った場合の切り戻しのActionが成功した場合のworkflowの結果はfailedではなく、succeededとしたいですよね。
そこでworkflowの成否を判定するためのフラグを用意しました。


```/opt/stackstorm/packs/mydemo_pack/actions/workflows/my-first-wf.yaml
version: 1.0

description: return code depends on the result

input:
  - distro_name
  - timeout

output:
  - failed: <% ctx().failed %>
  

tasks:
  init:   # failedフラグを初期化(False)とするだけのAction
    action: core.noop　　
    next:
    - publish:
        - failed: False
      do: main
  
  main:  # ディストリビューションを確認する本workflowメインのAction
    action: mydemo_pack.get_distro
    input:
      distro_name: <% ctx().distro_name %>
      timeout:  <% ctx().timeout %>
    next:
      - when: <% succeeded() %>
        do: post_msg
        publish:
          - failed: False
          - msg: "result: <% result() %>"
        # Actionはfailed、かつフラグのfaildはFalse(not True)、かつ引数のdistro_nameは'centos'であるとき
      - when: <% failed() and not ctx().failed and (ctx().distro_name = 'centos') %>
        do: main　　# distro_nameは'ubuntu' failedはTrueとしてmainを再実行
        publish:
          - distro_name: "ubuntu"
          - failed: True
      - when: <% failed() and ctx().failed %>
        do: post_msg
        publish:
          - msg: "result: <% result() %>"
  
  post_msg:
    action: slack.post_message
    input:
      message: <% ctx().msg %>
    next:
      - do: last
  # failedフラグがTrueである場合、orqeustaが用意する必ず結果がfailedになるfailというActionを実行し、
  # workflowの結果もfailedとする
  last: 
    action: core.noop
    next:
      - when: <% ctx().failed %>
        do: fail

```


AnsibleのPackを使うこともできます。

```
root@$HOSTNAME: # st2 run mydemo_pack.playbook-demo
............
id: 5df8cabfb530ad0122f3ad5a
action.ref: mydemo_pack.playbook-demo
parameters: None
status: succeeded
start_timestamp: Tue, 17 Dec 2019 12:31:59 UTC
end_timestamp: Tue, 17 Dec 2019 12:32:22 UTC
result: 
  output:
    msg:
      exit_code: 0
      result: true
      stderr: 'st2.actions.python.PostMessageAction: INFO     Message successfully posted
        '
      stdout: ''
+--------------------------+-------------------------+----------+------------+-----------------+
| id                       | status                  | task     | action     | start_timestamp |
+--------------------------+-------------------------+----------+------------+-----------------+
| 5df8cac0b530ad0035fd758b | succeeded (21s elapsed) | playbook | ansible.pl | Tue, 17 Dec     |
|                          |                         |          | aybook     | 2019 12:32:00   |
|                          |                         |          |            | UTC             |
| 5df8cad5b530ad0035fd758e | succeeded (0s elapsed)  | echo     | core.local | Tue, 17 Dec     |
|                          |                         |          |            | 2019 12:32:21   |
|                          |                         |          |            | UTC             |
| 5df8cad5b530ad0035fd7591 | succeeded (1s elapsed)  | last     | slack.post | Tue, 17 Dec     |
|                          |                         |          | _message   | 2019 12:32:21   |
|                          |                         |          |            | UTC             |
+--------------------------+-------------------------+----------+------------+-----------------+
root@$HOSTNAME: # 
```



stackstormのActionから、Juniper社のネットワークスイッチのQFXに対してこのようなshow versionを流すこともAnsible packを使えば可能です。

```
jcluser@Template-vQFX-Light> show version | no-more 
fpc0:
--------------------------------------------------------------------------
Hostname: Template-vQFX-Light
Model: vqfx-10000
Junos: 15.1X53-D63.9
JUNOS Base OS boot [15.1X53-D63.9]
JUNOS Base OS Software Suite [15.1X53-D63.9]
JUNOS Online Documentation [15.1X53-D63.9]
JUNOS Crypto Software Suite [15.1X53-D63.9]
JUNOS Packet Forwarding Engine Support (qfx-10-f) [15.1X53-D63.9]
JUNOS Kernel Software Suite [15.1X53-D63.9]
JUNOS Web Management [15.1X53-D63.9]
JUNOS Enterprise Software Suite [15.1X53-D63.9]
JUNOS SDN Software Suite [15.1X53-D63.9]
JUNOS Routing Software Suite [15.1X53-D63.9]
JUNOS py-base-i386 [15.1X53-D63.9]

{master:0}
jcluser@Template-vQFX-Light> 

```

Ansible Packのplaybook Actionの引数はinventory_fileとplaybookです。

```
root@$HOSTNAME: # st2 run ansible.playbook \
inventory_file=/opt/stackstorm/packs/ansible/inventory/hosts \
playbook=/opt/stackstorm/packs/ansible/playbook/gather_facts_junos.yaml 
....
id: 5df8d56eb530ad0122f3ad6f
status: succeeded
parameters: 
  cwd: /opt/stackstorm/packs/ansible
  inventory_file: /opt/stackstorm/packs/ansible/inventory/hosts
  playbook: /opt/stackstorm/packs/ansible/playbook/gather_facts_junos.yaml
result: 
  failed: false
  return_code: 0
  stderr: "/opt/stackstorm/virtualenvs/ansible/lib/python2.7/site-packages/cryptography/hazmat/primitives/constant_time.py:26: CryptographyDeprecationWarning: Support for your Python version is deprecated. The next version of cryptography will remove support. Please upgrade to a release (2.7.7+) that supports hmac.compare_digest as soon as possible.
  utils.PersistentlyDeprecated2018,
[WARNING]: arguments wait_for, match, rpcs are not supported when using
transport=cli"
  stdout: "
PLAY [junos] *******************************************************************

TASK [show version] ************************************************************
ok: [66.129.235.3]

PLAY RECAP *********************************************************************
66.129.235.3               : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
"
  succeeded: true
root@HOSTNAME: # 
```




## 8. ファイルの編集方法やトラブルシュートと次回予告
`ActionやWorkflowのメタファイルやWorkflowを編集`したら、st2 runで実行する前に`st2ctl reload`でyamlファイルの編集をStackstormに認識させください。
`Actionの実行スクリプト`(mydemo_pack/actions/scripts/*のスクリプト)の編集については`st2ctl reload`する必要はないです。


```
root@HOSTNAME: # st2ctl reload --register-all
```

もし、うまくパラメータが認識されないといったことがありましたら、yamlのインデントを下記のツールなどを使って調べてみてください。


[yamllint](http://www.yamllint.com/)


あるいは`/var/log/st2/st2api.log`を追ったり、`st2ctl status`でst2関連のステータスを確認することもありです。

```
root@HOSTNAME:/# tail -f /var/log/st2/st2api.log
2019-12-17 13:55:34,158 140522372926512 INFO logging [-] ca8bb925-9695-46f1-8031-6698aad411d9 - GET /v1/executions/5df8de45b530ad0122f3ad7e with query={} (remote_addr='127.0.0.1',method='GET',request_id='ca8bb925-9695-46f1-8031-6698aad411d9',query={},path='/v1/executions/5df8de45b530ad0122f3ad7e')
root@HOSTNAME:/# st2ctl status
##### st2 components status #####
st2actionrunner PID: 140
st2actionrunner PID: 178
st2api PID: 61
st2api PID: 282
st2stream PID: 64
st2stream PID: 287
st2auth PID: 49
st2auth PID: 314
st2garbagecollector PID: 47
st2notifier PID: 54
st2resultstracker PID: 51
st2rulesengine PID: 59
st2sensorcontainer PID: 46
st2chatops is not running.
st2timersengine PID: 63
st2workflowengine PID: 55
st2scheduler PID: 57
mistral-server PID: 491
mistral.api PID: 486
mistral.api PID: 523
mistral.api PID: 524
root@HOSTNAME:/# 
```

それでもダメでしたら、`st2ctl restart`を実行してください。Stackstorm関連の複数プロセスを再起動するか、Dockerコンテナ自体をビルドし直しましょう。
環境構築の手順をコード化しているので、ラクチンです。


```
root@HOSTNAME: # st2ctl restart
```

ホスト側で更新したファイルをコンテナに反映させるにはコンテナを削除してから改めてdocker-compose upする必要があるのですが、
面倒くさいのでシェルスクリプトを書きました。
-bをつけるとコンテナを削除した後、立ち上げ、-nをつけるとコンテナを削除するだけというものです。

```
ubuntu@st2-pub-1a ~/st2-docker-gkz (tutorial-install-pack) $ bash sh/setup.sh -b
(略)
Successfully built 373fcf9dbfcd
Successfully tagged stackstorm/stackstorm:3.1
Creating st2-docker-gkz_rabbitmq_1 ... done
Creating st2-docker-gkz_postgres_1 ... done
Creating st2-docker-gkz_redis_1    ... done
Creating st2-docker-gkz_mongo_1    ... done
Creating st2-docker-gkz_stackstorm_1 ... done
ubuntu@st2-pub-1a ~/st2-docker-gkz (tutorial-install-pack) $ docker-compose exec stackstorm bash
root@HOSTNAME:/# 

```

getopsを使ったのは個人的に書いてみたかったからというだけです笑。

```/st2-docker-gkz/sh/setup.sh
#!/bin/bash

function help {
  cat <<- EOF
    overview：wheter run "docker-compose up -d --build", or not
    usage：bash sh/setup.sh [-h|-b|-n|
    option：
      -h  this message
      -b ->> run "docker-compose up -d --build", after removing all contrainers
      -n ->> run "nothing" after removing them
EOF
    exit 1

}

while getopts ":bn" OPT;

do
    case ${OPT} in
        b)
          BUILD_FLAG=1;
            ;;
        n)
          BUILD_FLAG=0;
            ;;
        \?)
          help
            ;;
    esac
done

shift $((OPTIND - 1))

IDS=$(docker container ls -aq)

for i in $IDS
do
  echo "$i"
  docker container stop $i && docker container rm $i
done

#IMAGES=$(docker images -aq)
#for i in $IMAGES
#do
#  echo "$i"
#  docker image rmi "$i"
#done

docker container prune --force
docker image prune --force
docker volume prune --force

docker-compose down -v

if [ "$BUILD_FLAG" -eq 1  ]; then
  echo 'Run "docker-compose up -d --build"'
  docker-compose -f docker-compose-dev.yml up -d --build
elif [ "$BUILD_FLAG" -eq 0 ]; then
  echo 'Not Run "docker-compose up -d --build"'
else
  echo "option is unexpected."
  help
fi
```


本当はここからActionの具体的な編集方法やワークフローの定義のやりかたに関するサンプルコードを載せたかったのですが、
力尽きたので次回に譲ります、、。


Stackstormの魅力、ちょっとworkflow書いてみようかなと思っていただけたらうれしいです。


第二部できました！！
[Dockerで始めるStackstorm再入門2/3(条件分岐させるWorkflowと定期実行させるRuleの書き方)](https://qiita.com/gkzz/private/85f119fc7771d5de582d)



## 参考

- [Actions StackStorm 3.1.0 documentation](https://docs.stackstorm.com/actions.html)


- [Ubuntu Linux を実行しているプライベート Amazon EC2 インスタンスに静的ホスト名を割り当てる]
(https://aws.amazon.com/jp/premiumsupport/knowledge-center/linux-static-hostname)

- [シェルスクリプトのreturn - 考える人、コードを書く人]
(https://bokko.hatenablog.com/entry/20090223/1235400626)

- orqeustaのサンプルコード

  - [github.com/StackStorm/st2/blob/master/contrib/examples/actions/workflows/orquesta-streaming-demo.yaml]
(https://github.com/StackStorm/st2/blob/master/contrib/examples/actions/workflows/orquesta-streaming-demo.yaml)

  - [github.com/StackStorm/st2/blob/master/contrib/examples/actions/local_command_runner_print_to_stdout_and_stderr.yaml](https://github.com/StackStorm/st2/blob/master/contrib/examples/actions/local_command_runner_print_to_stdout_and_stderr.yaml)

- Ansible pack
  - [StackStorm と Ansible で Cisco IOS ルーターに設定を投入する（st2 run編）](https://tekunabe.hatenablog.jp/entry/2018/05/19/st2_run_ansible_ios)

- Slack pack
  - [StackStorm 経由で Slack に投稿する（準備 ～ st2 run編）](https://tekunabe.hatenablog.jp/entry/2018/05/28/st2_run_slack)


- 本記事で扱ったStackstormのサンプルコード
  - [gkzz/st2-docker-gkz](https://github.com/gkzz/st2-docker-gkz)

- Stackstormの管理対象のアプリコンテナ
  - [gkzz/flask-docker](https://github.com/gkzz/flask-docker)

- ActionとWorkflowのサンプルコード
  - [gkzz/mydemo_pack](https://github.com/gkzz/mydemo_pack)


## P.S. Twitterもやってるのでフォローしていただけると泣いて喜びます:)
[@gkzvoice](https://twitter.com/gkzvoice)

[#gkz](https://twitter.com/hashtag/gkz)


## P.P.S. StackstormでLTしました！

[StackstormというIFTTT的なツールをDockerコンテナに載せた小話](https://speakerdeck.com/gkzz/stackstormtoiuiftttde-naturuwodockerkontenanizai-setaxiao-hua)