Automation PilotでHANA Cloudインスタンスを起動するコマンドを自分で作ってみる

## はじめに
以前、Automation Pilotを使ってHANA Cloudインスタンスの起動を自動化する記事を書きました。

https://qiita.com/tami/items/fb369b2a34b878b29064

このときはチュートリアルで提供されたコマンドを使用し、コマンドがどのように定義されているかは深掘りしませんでした。Automation Pilotの仕組みをもう少し知りたいと思ったので、今回は事前定義されたコマンドを参考に自分でコマンドを作成してみることにしました。

以下ではまず、チュートリアルで提供されたコマンドの定義を確認し、それを参考に自分でコマンドを作成します。コマンド作成のところだけに関心がある方は、「[コマンドを作成する](https://qiita.com/tami/items/840383ec6c9cf8676ede#%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B)」セクションからお読みください。

## チュートリアルで提供されたコマンドを確認
前回の記事で使用したのは、以下のチュートリアルにあったカタログです。

https://developers.sap.com/tutorials/hana-cloud-automation-pilot.html#61cc4768-f695-4179-9643-c4a4a7888c61

そのときに使用した`StartHC`というコマンドに関連する部分について見てみます。

```json
...
  "commands": [
    {
      "configuration": {
        "values": [
          {
            "alias": "InstanceDetails",
            "valueFrom": {
              "inputReference": "Automation-<<<TENANT_ID>>>:InstanceDetails:1",
              "inputKey": null
            }
          },
          {
            "alias": "start",
            "valueFrom": {
              "inputReference": "Automation-<<<TENANT_ID>>>:Start:1",
              "inputKey": null
            }
          }
        ],
        "output": {},
        "executors": [
          {
            "execute": "sm-sapcp:UpdateServiceInstance:1",
            "input": {
              "instanceId": "$(.InstanceDetails.instanceId)",
              "displayName": "$(.InstanceDetails.instanceName)",
              "serviceKey": "$(.InstanceDetails.smBindingKey)",
              "parameters": "$(.start.parameters)"
            },
            "alias": "update",
            "progressMessage": null,
            "initialDelay": null,
            "pause": null,
            "when": null,
            "validate": null,
            "autoRetry": null,
            "repeat": null,
            "errorMessages": []
          }
        ],
        "listeners": []
      },
      "id": "Automation-<<<TENANT_ID>>>:StartHC:1",
      "name": "StartHC",
      "description": "Start an SAP HANA Cloud instance provisioned to an SAP BTP subaccount",
      "catalog": "Automation-<<<TENANT_ID>>>",
      "version": 1,
      "inputKeys": {},
      "outputKeys": {},
      "tags": {}
    }  
  ]
```
ここから、次のようなことがわかります。
- `configuration`セクションで`InstanceDetails`、`start`という2つのパラメータを定義している
- これらは、同じカタログ内で定義されたinputと呼ばれるオブジェクトを参照している
- `sm-sapcp:UpdateServiceInstance:1`というコマンドを実行している
- コマンドに`configuration`セクションで定義したオブジェクトの値を渡してる

`InstanceDetails`、`start`の定義も同じカタログにあります。

```json
...
  "inputs": [
    {
      "id": "Automation-<<<TENANT_ID>>>:InstanceDetails:1",
      "name": "InstanceDetails",
      "description": "The details of an SAP HANA Cloud Instance deployed to an SAP BTP subaccount",
      "catalog": "Automation-<<<TENANT_ID>>>",
      "owner": null,
      "version": 1,
      "keys": {
        "instanceId": {
          "type": "string",
          "sensitive": false,
          "description": "The instance ID"
        },
        "instanceName": {
          "type": "string",
          "sensitive": false,
          "description": "The name of an SAP HANA Cloud instance"
        },
        "instanceBindingKey": {
          "type": "object",
          "sensitive": false,
          "description": "An optional binding key on an SAP HANA Cloud Instance used to access alerts and metrics"
        },
        ...
      },
      "values": {
        "instanceId": "b00eac16-5486-4f27-a52b-359f250273c3",
        "instanceName": "HC_HDB",
        "instanceBindingKey": "{}",
        "servicePlanId": "e573479c-39f8-4774-80a9-12d762b0f159",
        "smBindingKey": "{}"
      },
      "tags": {}
    },
    ...
     {
      "id": "Automation-<<<TENANT_ID>>>:Start:1",
      "name": "Start",
      "description": "Start JSON for an SAP HANA Cloud instance",
      "catalog": "Automation-<<<TENANT_ID>>>",
      "owner": null,
      "version": 1,
      "keys": {
        "parameters": {
          "type": "object",
          "sensitive": false,
          "description": "JSON parameter to start an SAP HANA Cloud instance"
        }
      },
      "values": {
        "parameters": "{\n    \"data\": {\n        \"serviceStopped\": false\n    }\n}"
      },
      "tags": {}
    }   
  ]  
```

次に、上記のコマンドがAutomation PilotのUIでどのように見えるか確認します。

### インプットの定義
"Automation-T001565R7"というカタログにインプットが定義されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/4593efec-f292-c825-dfd8-e94b8ae85644.png)

`InstanceDetails`の定義は以下です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/8555c443-84ae-4b0a-24e3-c5513c80aa1f.png)

`start`の定義は以下です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/7d4e029f-fa16-2d98-6d47-fa8fd23e00ec.png)

### コマンドの定義
コマンドは**input**、**output**、そして任意の数の**executor**（以下の例では"update"というエイリアスを持つコマンド）で構成されます。

#### input
inputでは、コマンドへの入力を定義します。コマンドへの入力には、"Input Key"と"Additional Value"の2種類があります。"Input Key"はコマンドを実行するときにユーザが入力するもの、"Additional Value"は事前に設定しておくもので、ユーザが入力する必要がありません。"Additional Value"には定義しておいたインプットを指定します。ここではAdditional Valuesとして`InstanceDetails`、`start`が定義されています。

`StartHC`コマンドの場合、Input Keyは使っておらず、全て事前に定義しておく形となっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/e9ec8299-d580-edd9-a857-433784fda3a5.png)

#### executor (update)
executorはコマンドの呼び出しです。ここで定義されているupdateは、`sm-sapcp:UpdateServiceInstance:1`というコマンドを実行します。これは[事前定義されているコマンド](https://help.sap.com/docs/automation-pilot/automation-pilot/provided-commands)の一つで、（Cloud Foundryではなく）BTPサブアカウントに作成したHANA Cloudインスタンスを操作するために使います。このコマンドを直接実行してもよいのですが、認証情報などを毎回指定するのが煩雑なのでカスタムのコマンドでラップしているわけです。
Parametersセクションで、このコマンドが持っているパラメータとinputブロックで受け取ったパラメータのマッピングをしています。

※事前定義されたコマンドの中身が気になるところですが、インプット、アウトプットのみ確認可能でスクリプトの定義は見ることができないようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/a290d16e-7bd5-11a9-cfb7-56e58c01e386.png)

Additional Valuesで指定した項目は、以下のように参照できます。
        `$(.<alias>.<key>)`
例：`$(.InstanceDetails.instanceId)`

Input Keyで指定した項目は、以下のように参照できます。
`$(.execution.input.<key>)`


#### output
outputはコマンドから出力する値の定義です。`StartHC`コマンドには、アウトプットがありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/da3acdb8-5506-91f4-5c2b-f051669d2e3a.png)

他のコマンド（例：GetInstanceParameters）を見てみると、Output Keysで出力用パラメータを定義し、Output Valuesでexecutorのアウトプットをマッピングしていました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/c7d84eb8-6a74-7157-0941-0761fe437c8f.png)

※Auomation Pilotでは"Example"とつくコマンドが用意されており、自分でコマンドを作成する際の参考にすることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/53477ee3-bf72-75a8-ca3f-b93409d42dce.png)

## コマンドを作成する
`StartHC`コマンドを参考に、HANA Cloudインスタンスを起動、停止するコマンドを作成します。

### ステップ
1. カタログを作成
1. インプットを作成
1. コマンドを作成
1. コマンドを実行

### 1. カタログを作成
はじめにインプット、コマンドを入れる器であるカタログを作成します。

My Catalogsメニューから"Create"をクリックして登録します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/6504102e-d563-452c-3aa4-049e3740357c.png)

Name, Display nameを入力して"Create"をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/6a5ee2da-c175-54dc-3596-9f22d3f078af.png)

### 2. インプットを作成
Inputメニューから"Create"をクリックして登録します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/0d0c9cec-a515-0aa2-9b40-25d6f84fbb4d.png)

Catalog, Nameを指定して"Create"をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/0f3c93b5-6106-74db-12d9-9dd51434eab8.png)

"Add"をクリックしてプロパティを追加していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/854de15c-4eda-6f82-b469-f32bd3095c89.png)

以下は`sm-sapcp:UpdateServiceInstance:1`コマンドのインプットです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/ee66bf7e-26fe-957a-3202-44c5a50843d3.png)
ここでは、そのうち以下の3つの項目を設定するためのインプットを定義します。
- instanceId
- serviceKey (smBindingKey)
- displayName (instanceName)


定義する内容は以下のようになります。

- instanceId
ValueにはHANA CloudのインスタンスIDを設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/73b2c3bc-828f-6ec2-4380-667f24635dca.png)

- instanceName
ValueにはHANA Cloudのインスタンス名を設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/b02ee154-e0a6-8abe-10d6-5bacdaee42ad.png)

- smBindingKey
ValueにはService Managerのサービスインスタンスのキーを貼り付けます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/6cd8062f-cb38-4382-04a7-84b228a246ea.png)

3つのプロパティを定義した状態です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/a4ac3e46-0199-4223-b3ec-3476f187a9c6.png)

### 3. コマンドを作成
Commandsメニューから"Create"をクリックして登録します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/5fe22720-e074-13e6-b764-b88694bb29a1.png)

Catalog, Nameを指定して"Create"をクリックします。今回は、起動と停止の両方で使えるコマンドを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/0a6738ff-20b1-d983-8e6f-082bac87a0b0.png)

#### 3.1. inputの定義
inputに渡すパラメータを定義します。まず、インスタンスを起動するか停止するかを入力パラメータとして渡したいので"Input Keys"に定義します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/5e5a421d-e605-6e85-af7a-e7346df4e415.png)

boolean型の項目`start`を定義し、`true`が渡された場合にHANA Cloudを起動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/d2191373-be17-4cfe-f577-16674814c74c.png)

Default Value Sourceを"Static"にすることで、デフォルト値を指定できます。実行時にインプットが指定されなかった場合、この値が使われます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/69583a60-637d-d47e-09b5-288b571a6361.png)

次に、Additional Valuesに前のステップで定義したインプットを指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/df6031aa-6f44-b275-46c8-44236d5bca98.png)

Aliasにはこのコマンドの中で参照する名前を指定します。Value Typeを"Input", Inputに前のステップで定義したインプットを指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/a2260aa3-f6bb-a7a7-873d-b6b3233be3c2.png)

#### 3.2. executorの定義
事前定義されたコマンドを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/2aacdb3c-34b2-b5d0-3387-c8cfcff3b6a2.png)

Aliasにはこのコマンドの中で参照する名前（ここでは"update"）を指定します。Commandに`sm-sapcp:UpdateServiceInstance:1`を指定します。"Here"という文字を選択して緑になった状態で追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/da6a36e3-34aa-9482-a61f-4fc6fc75ce64.png)

次に、コマンドへ渡すパラメータへのマッピングを行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/fdf97a2e-94d9-6a1d-e10a-72b6e9059024.png)

以下のようにマッピングします。
|キー  |設定値  |
|---|---|
|instanceId  |`$(.InstanceDetails.instanceId)`  |
|serviceKey  |`$(.InstanceDetails.smBindingKey)`|
|displayName |`$(.InstanceDetails.instanceName)`|
|parameters | `{ "data": { "serviceStopped": $(if .execution.input.start == true then false else true end) } }`|

parametersには以下の形式のオブジェクトを渡します。インスタンスを停止する場合は"serviceStopped"に`true`、起動する場合は`false`を指定します。インプットパラメータである`start`を反転させた値を設定しなければならないので、[Dynamic Expression](https://help.sap.com/docs/automation-pilot/automation-pilot/dynamic-expression)と呼ばれる式を使っています。

```json
{
    "data": {
        "serviceStopped": false
    }
}
```
※参考にしたカタログでは、上記をそのままInputとして定義していました。式を使うよりその方が運用上はわかりやすいと思います。今回は式を使ってみたかったのであえてインプットをプリミティブ型にしました。

#### 3.3. outputの定義
コマンドが出力するパラメータを定義します。参考にしたコマンド`StartHC`にはアウトプットがありませんが、せっかくなので`sapcp:UpdateServiceInstance:1`のアウトプットをそのまま出力してみます。

`sapcp:UpdateServiceInstance:1`のアウトプットは以下のように定義されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/fb66b34d-b6cc-9fc8-93c6-68e4d417a2fb.png)

Output Keysのセクションで"Add"をクリックしてアウトプットを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/40ca1d29-aad7-97c2-09df-3994400016cd.png)

一つ目のアウトプットは`parameters`という名前のオブジェクトです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/5628d082-2a8a-316b-a7d8-99a7fed4bcc7.png)

 二つ目のアウトプットは`serviceInstance`という名前のオブジェクトです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/4a17e835-4362-87b3-60c8-03b7b223d523.png)

定義したアウトプットに`sapcp:UpdateServiceInstance:1`のアウトプットをマッピングします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/81bb0335-46ff-71a2-bc9a-363934f6f03b.png)

executorのアウトプットを参照するには、`$(<alias>.output.<key>)`のように定義します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/b84bd59a-a248-315f-5962-303b9b1c4fdf.png)


### 4. コマンドを実行
"Trigger"をクリックしてコマンドを実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/ec1f28b2-7734-3eff-6a36-e0f97e5564c5.png)

パラメータ`start`に`false`を指定します。これは起動しているインスタンスを停止する操作になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/b08e690b-362c-80b0-b7af-74f6e35055a9.png)

コマンドを実行すると、HANA Cloudのインスタンスが停止しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/13309120-f114-5b7b-29e2-e27eb2f69030.png)

コマンドのステータスが"FINISHED"となって正常に終了しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/5bd41e90-ba41-b166-b214-a908e62a9543.png)

アウトプットされた値を確認します。parameters, serviceInstanceいずれも多数の項目を持っていることがわかりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/c4a2dba4-964f-fb6b-b1bc-5926dc738f34.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/9f7cb52e-1a6e-848f-7ed6-c5d31a285b49.png)

一部の項目のみに関心がある場合は、`$(.update.output.serviceInstance.id)`のようにプロパティまで指定することで、対象の項目を取得できます。

## まとめ
- Automation Pilotで使用するオブジェクトは、カタログ、インプット、コマンドの3つ
- インプットは、コマンド実行の際に渡したいパラメータを定義するオブジェクト
- コマンドは、input、excecutor、outoputから構成される
- executorには任意の数のコマンド（事前定義済みのコマンドおよび自分で作成したコマンド）を指定できる
- コマンドには、Dynamic Expressionを使用してインプットの値や先行のコマンドのアウトプットをマッピングして渡すことができる