IBM OpenWhiskというサーバーレス実行環境を用いて、Swiftでおみくじ🎍

クラウドサービスは、AWS・Azure・GCPなどありますが、Swiftに対応しているサービスがあるのは現状[IBM Bluemix](https://console.ng.bluemix.net/)のみのはずです(他のサービスでも仮想UbuntuマシンでSwift実行環境を整えることなどはできるがそれは除く)。

本記事では、そのうち[OpenWhisk](https://developer.ibm.com/openwhisk/)という、サーバーレス実行環境をSwiftで使ってみます。他のクラウドサービスの以下に相当するサービスです。

- [AWS Lambda](https://aws.amazon.com/lambda/details/)
- [Azure Functions](https://azure.microsoft.com/en-us/services/functions/)
- [Google Cloud Functions](https://cloud.google.com/functions/)

Swiftが使える以外にも[オープンソース公開されている](https://github.com/openwhisk/openwhisk)という特徴があります。そのため、Issue・Pull Requestを投げたり、自前サーバーに環境を構築することができます。

しばらくベータ版でしたが、昨年2016年12月にめでたく正式リリースされました🎉
[General Availability: What's new in IBM Bluemix OpenWhisk? - Bluemix Blog](https://www.ibm.com/blogs/bluemix/2016/12/general-availability-openwhisk/)

# Swiftでおみくじを作ってみる

では、Swiftでおみくじを作っていきます。

## IBM Bluemixコンソールにアクセスできるようにする

[IBM Bluemixコンソール](https://console.ng.bluemix.net)にアクセスして、アカウント登録します。30日間はリソースに制限はありつつもほぼすべての機能を無料で試用できます。それを超えると、クレジットカード登録が必要です。他のクラウドサービスと同様、[Billing](https://console.ng.bluemix.net/account/#billing/)で意図しない課金が発生していないか気を付けながら使いましょう。

OpenWhiskの価格は、 [OpenWhisk - Pricing](https://console.ng.bluemix.net/openwhisk/learn/pricing) に記載されています。

- 無料枠: 毎月40万GB-seconds
- 超過分: $0.000017/1GB-second

参考: [クラウドサービスIBM Bluemixを無料で使うノウハウまとめ - Qiita](http://qiita.com/zuhito/items/86e7ad47d14937d3e9b6)

## OpenWhiskを使ってみる

[OpenWhisk](https://console.ng.bluemix.net/openwhisk/)がトップページです。
[OpenWhisk - Editor](https://console.ng.bluemix.net/openwhisk/editor)にてブラウザ上でコードを書いたり操作したりできますが、本記事ではコマンドライン中心にやっていくので、[OpenWhisk - CLI](https://console.ng.bluemix.net/openwhisk/learn/cli)にて、`wsk`というコマンドラインツールをダウンロード設定していきます。

### コマンドラインツールのインストール

僕はダウンロードして`mv wsk /usr/local/bin`にて配置しました。`pip`インストールする方法もあるようですが、数ヶ月前の記事の通りやったら404エラーで、確かな方法が分からなかったので諦めました。

### `Authorization Key`のセット

[OpenWhisk - CLI](https://console.ng.bluemix.net/openwhisk/learn/cli)に書いてあるコマンドをコピペして実行します。


```sh
$ wsk property set --apihost openwhisk.ng.bluemix.net --auth ㊙️ひみつのauthentication key㊙️
```

### 設定ができたか確認

こちらを入力して、

```sh
$ wsk action invoke /whisk.system/utils/echo -p message hello --blocking --result
```

こちらが返ってくれば成功です。

```json
{
    "message": "hello"
}
```

オプションの意味を、`wsk action invoke --help`コマンドで確認してみます。

> `-b, --blocking          blocking invoke`

デフォルトでは呼び出すだけで結果が返ってくるのを待たないようなので、結果を確認したい場合必要のようです。

> `-r, --result            show only activation result if a blocking activation (unless there is a failure)
`

`activation result`のみを得たい時のオプションですね。省くと次のような冗長なレスポンスとなりました。

```json
ok: invoked /whisk.system/utils/echo with id 7503c91d5d104b1c99583960aaae2b56
{
    "namespace": "メールアドレス_mono",
    "name": "echo",
    "version": "0.0.41",
    "subject": "メールアドレス",
    "activationId": "7503c91d5d104b1c99583960aaae2b56",
    "start": 1483419817114,
    "end": 1483419817117,
    "duration": 3,
    "response": {
        "status": "success",
        "statusCode": 0,
        "success": true,
        "result": {
            "message": "hello"
        }
    },
    "logs": [],
    "annotations": [
        {
            "key": "limits",
            "value": {
                "logs": 10,
                "memory": 256,
                "timeout": 60000
            }
        },
        {
            "key": "path",
            "value": "whisk.system/utils/echo"
        }
    ],
    "publish": false
}
```

## OpenWhiskのActionを作成

Web UIでやる場合は前述の通り、[OpenWhisk - Editor](https://console.ng.bluemix.net/openwhisk/editor)を弄っていけば良いですが、今回は`wsk`コマンドでやっていきたいので、[Getting started with OpenWhisk](https://console.ng.bluemix.net/docs/openwhisk/index.html)を参照していきます。

[Creating and invoking OpenWhisk actions](https://console.ng.bluemix.net/docs/openwhisk/openwhisk_actions.html)の項目にActionの作成の仕方が書いてあります。

### Swiftでおみくじロジックを書く

OpenWhiskで呼び出せるSwiftスクリプトは、以下のように`args: [String:Any]`を受けて、`[String:Any]`を返す`main`関数を用意する必要があるようです。

```swift
func main(args: [String:Any]) -> [String:Any] {
    // ここに何か書く
}
```

というわけで書きました。

```swift
import Foundation
// https://gist.github.com/rymcol/48a505c2a1c874daea52a296a2687f5f (そのままではランダムにならなかったので、さらに工夫)
#if os(Linux)
    private func arc4random_uniform(_ max: UInt32) -> Int32 {
        srand(UInt32(NSDate().timeIntervalSinceReferenceDate))
        return rand() % Int32(max-1)
    }
#endif

private func random(_ upper: Int) -> Int {
    return Int(arc4random_uniform(UInt32(upper))) % upper
}
// 文字化けするのでアルファベット🤕
//private let omikujis = ["大吉😋", "中吉💋", "小吉🐶", "吉😕", "末吉🤕", "凶😡", "大凶🔪"]
private let omikujis = ["daikichi", "chukichi", "shokichi", "kichi", "suekichi", "kyo", "daikyo"]
private func doOmikuji() -> String {
    return omikujis[random(omikujis.count)]
}

func main(args: [String:Any]) -> [String:Any] {
    // 文字化けするのでアルファベット🤕
//    let r = ["Swiftおみくじの結果です": doOmikuji()]
    let r = ["Swift Omikuji": doOmikuji()]
    print(r)
    return r
}
```

コメントに書いてありますが、Linux環境用に`arc4random_uniform(_:)`関数を用意する必要がありました🤕
また、日本語が文字化けしてしまい、解決法が分からなかったのでとりあえずアルファベットにしました🤕

### Action作成・更新

`omikuji`という名前でアクションを作成します。

- 作成: `wsk action create omikuji omikuji.swift`
	- 初回
	- `wsk action list`で`omikuji`が一覧されることを確認
- 更新: `wsk action update omikuji omikuji.swift`
	- 2回目以降

### Action実行

上で`/whisk.system/utils/echo`を呼びましたが、それと同様に次のように呼べます。

```sh
wsk action invoke omikuji --blocking --result
```

うまくいくと、次のような結果が返ってきます。

```json
{
    "Swift Omikuji": "chukichi"
}
```

エラーが発生したら、`--result`を外して詳細を確認しましょう。

## HTTPアクセスしてみる

### Experimantal版の簡単な方法

[Exposing your actions via API Gateway (Experimental)](https://console.ng.bluemix.net/docs/openwhisk/openwhisk_apigateway.html#openwhisk_apigateway)に書いてあります。

次のコマンドを実行すると、HTTPアクセス可能なURLを生成してくれます。

```sh
$  wsk api-experimental create /omikuji get omikuji
```

こちらでおみくじできます🎍:
https://8305f020-572f-4eed-acd1-d8887bee03c7-gws.api-gw.mybluemix.net/omikuji


### REST APIを用いた方法

以下の情報を元にがんばりました。

- [OpenWhisk system details](https://console.ng.bluemix.net/docs/openwhisk/openwhisk_reference.html#openwhisk_ref_restapi)
- [IBM Bluemix - IBM Bluemix OpenWhisk](https://console.ng.bluemix.net/apidocs/98?&language=node#introduction)
- [IBM Bluemix OpenWhisk REST API | Takehiko Amano's Blog on Emerging Technologies.](https://amanoblog.wordpress.com/2016/03/03/ibm-bluemix-openwhisk-rest-api/?cm_mc_uid=56490082623414691585059&cm_mc_sid_50200000=1483417058)
	- 認証などちょっと古いので上のドキュメントの補足として読む

ちょっと手こずりましたが、以下のようにするといけました。

認証情報を確認:


```
$ cat ~/.wskprops
→ AUTH=㊙️
```

`curl`でPOSTリクエスト:

```
curl -u ㊙️ -X POST --data '{}' -H "Content-Type: application/json"  https://openwhisk.ng.bluemix.net/api/v1/namespaces/名前空間名/actions/omikuji\?blocking\=true\&result\=true
```

---

ちょちょいとやるつもりが、けっこう手こずりました🤕
`wsk action create`コマンドで、多分単一Swiftファイルしか指定できないようでライブラリも使えなさそうなので、そうなるとけっこう利用シーンが限定されるかなと思いました🤔
Lambda系サービスははそういうもんなのかもしれないですが、とはいえもうちょっと自由が欲しいなと思いました。
でも大体使えるようになったので、[連携機能](https://console.ng.bluemix.net/openwhisk/learn/integrations)使ったり実利用してみたりしたいなと思っています(　´･‿･｀)


# 関連リンク

- [Ben Snider – Getting Started with OpenWhisk Server Side Swift](http://www.bensnider.com/getting-started-with-openwhisk-server-side-swift.html)
- [IBM Bluemix OpenWhisk REST API | Takehiko Amano's Blog on Emerging Technologies.](https://amanoblog.wordpress.com/2016/03/03/ibm-bluemix-openwhisk-rest-api/?cm_mc_uid=56490082623414691585059&cm_mc_sid_50200000=1483417058)
- [オープンソースのサーバレス実行環境「OpenWhisk」の概要とアプリケーションの作成 (1/5)：CodeZine（コードジン）](http://codezine.jp/article/detail/9623)
- [OpenWhisk Advent Calendar 2016 - Qiita](http://qiita.com/advent-calendar/2016/openwhisk)
- [Bluemixアドベントカレンダーに参加しよう！｜teratail（テラテイル）](https://teratail.com/Bluemix)