LoginSignup
8

More than 5 years have passed since last update.

posted at

IBM Cloud Functions(OpenWhisk)を使いNode-RED Flow定義をバージョン管理する

こんにちは、水津です。
この記事は、IBM Cloud Advent Calendar 2017 23日目の投稿です。

はじめに

Node-REDで開発する際に困るのが、フローのバージョン管理。特に複数人で開発していると、間違って更新し他人の更新を消しちゃうことも。今回はそんな悩みを解決するために、IBM Cloud上のFunctionsとNode-REDを活用し、Node-REDのフロー定義更新を自動的にGitに反映する、Node-RED Flowの簡易的なバージョン管理システムを作りたいと思います。

構成

今回はこんな構成での実装を行います。
スクリーンショット 2017-12-23 23.02.23.png

IBM GitLab の準備

まずは、Flow定義の保存先(バージョン管理先)となるGitLabのリポジトリを準備します。今回は、IBM Cloudが提供してますGitLab環境を用いたいと思います。

  • テキトウな名称で新規リポジトリを作成ください。
  • 作成後、リポジトリの「Project ID」をメモください。(あとで、API CALLの設定を行う際、使用します) スクリーンショット 2017-12-23 23.09.41.png
  • アカウントの「Personal Access Token」を作成し、トークンをメモください。(あとで、API CALLの設定を行う際、使用します) スクリーンショット 2017-12-23 23.17.47.png

これでGitLabの準備は完了です。

IBM Cloud Functions(OpenWhisk)の実装

続いて、Node-REDのFlow定義が保存されているCloudantの更新を検知し動かす、「Functions(機能)」の実装を行っていきたいと思います。

  • 「機能」の開始 > 概要より、「作成の開始」をクリックし、まずはシーケンスを作成します。 スクリーンショット 2017-12-23 23.22.15.png
  • 任意のシーケンス名を入力、「Use Public」の「Websocket」を選択し、必要パラメータを入力します。(「uri」は後ほど行うNode-RED実装のWebsocket uriと同じ値にします) スクリーンショット 2017-12-23 23.30.08.png
  • シーケンスのアクション一覧にて「Add」を押下し、もう一つアクションを追加します。 スクリーンショット 2017-12-23 23.36.30.png

アクションのコードは以下のように定義ください。

  function main(params) {
     var msg = {};
     msg.payload = params.id;
     return msg;
  }

その後、以下のような順番で保存ください。
スクリーンショット 2017-12-23 23.47.10.png

  • 続いて、作成したシーケンスにトリガーを追加します。追加するトリガーは、「Cloudant」を選択ください。 スクリーンショット 2017-12-23 23.51.50.png

登録し終わると、以下のようになります。
スクリーンショット 2017-12-23 23.54.11.png

これで、CloudantのUpdateを検知し、WebsocketでUpdateされたDocumentのIDをSendする、Functions(機能)の実装が完了しました。

Node-RED の実装

続いて、Functions(機能)から送られてきたDocumentのIDをもとに、Flow定義をGitにPushするNode-REDのフローを定義します。

  • まずは、Node-REDにGitLabに接続するためのNodeを追加します。パレットの管理より、ノードの追加で「gitlab」を検索し、node-red-contrib-gitlab ノードを追加ください。(私の自作ノードです)
    スクリーンショット 2017-12-24 0.01.55.png

  • その後、以下のようにフローを定義ください。
    スクリーンショット 2017-12-24 0.05.15.png

フロー定義は以下になります。


[
    {
        "id": "f457b9b7.dea6d",
        "type": "websocket in",
        "z": "4ab9fe27.86f9c8",
        "name": "",
        "server": "166ec69b.fef401",
        "client": "",
        "x": 110,
        "y": 100,
        "wires": [
            [
                "80dfb138.ecced",
                "1b444420.62923c"
            ]
        ]
    },
    {
        "id": "80dfb138.ecced",
        "type": "debug",
        "z": "4ab9fe27.86f9c8",
        "name": "",
        "active": true,
        "console": "false",
        "complete": "true",
        "x": 270,
        "y": 100,
        "wires": []
    },
    {
        "id": "b03e8025.2dc4f8",
        "type": "debug",
        "z": "4ab9fe27.86f9c8",
        "name": "",
        "active": true,
        "console": "false",
        "complete": "false",
        "x": 650,
        "y": 400,
        "wires": []
    },
    {
        "id": "6972a335.8a49f4",
        "type": "GitLab-Update-RepositoryFile",
        "z": "4ab9fe27.86f9c8",
        "name": "",
        "branch_name": "master",
        "encoding": "text",
        "gitlabconfig": "fa916f31.d3a12",
        "x": 430,
        "y": 400,
        "wires": [
            [
                "b03e8025.2dc4f8"
            ]
        ]
    },
    {
        "id": "d8082f39.a124",
        "type": "debug",
        "z": "4ab9fe27.86f9c8",
        "name": "",
        "active": true,
        "console": "false",
        "complete": "payload",
        "x": 1030,
        "y": 540,
        "wires": []
    },
    {
        "id": "87c6cbda.c168c",
        "type": "catch",
        "z": "4ab9fe27.86f9c8",
        "name": "",
        "scope": [
            "6972a335.8a49f4"
        ],
        "x": 360,
        "y": 440,
        "wires": [
            [
                "4d03b085.c2dcc",
                "f4070782.4803a8"
            ]
        ]
    },
    {
        "id": "4d03b085.c2dcc",
        "type": "switch",
        "z": "4ab9fe27.86f9c8",
        "name": "",
        "property": "_error.statusCode",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "400",
                "vt": "num"
            },
            {
                "t": "else"
            }
        ],
        "checkall": "true",
        "outputs": 2,
        "x": 390,
        "y": 560,
        "wires": [
            [
                "5055bcc.3113cc4"
            ],
            [
                "9780af03.3d19f"
            ]
        ]
    },
    {
        "id": "9780af03.3d19f",
        "type": "debug",
        "z": "4ab9fe27.86f9c8",
        "name": "",
        "active": true,
        "console": "false",
        "complete": "true",
        "x": 530,
        "y": 580,
        "wires": []
    },
    {
        "id": "7dde70e9.f74138",
        "type": "GitLab-Create-RepositoryFile",
        "z": "4ab9fe27.86f9c8",
        "name": "",
        "branch_name": "master",
        "encoding": "text",
        "gitlabconfig": "fa916f31.d3a12",
        "x": 820,
        "y": 540,
        "wires": [
            [
                "d8082f39.a124"
            ]
        ]
    },
    {
        "id": "bee7ea4.93eca18",
        "type": "comment",
        "z": "4ab9fe27.86f9c8",
        "name": "IBM Cloud Functions からMSG受信",
        "info": "",
        "x": 180,
        "y": 60,
        "wires": []
    },
    {
        "id": "9c5b58f8.0a78d",
        "type": "cloudant in",
        "z": "4ab9fe27.86f9c8",
        "name": "Flow定義を取得",
        "cloudant": "",
        "database": "nodered",
        "service": "KS-Test-20171211-cloudantNoSQLDB",
        "search": "_id_",
        "design": "",
        "index": "",
        "x": 440,
        "y": 140,
        "wires": [
            [
                "26d24ca.e909734"
            ]
        ]
    },
    {
        "id": "947eff54.91dc6",
        "type": "function",
        "z": "4ab9fe27.86f9c8",
        "name": "Git PUSH MSG作成",
        "func": "var payloadMessage = {\n    \"file_path\": msg.process._id + \".json\",\n    \"content\": msg.payload,\n    \"commit_message\": \"Update Node-RED Flow\"\n};\n\nmsg.payload = payloadMessage;\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 480,
        "y": 260,
        "wires": [
            [
                "7707921c.c4755c",
                "6972a335.8a49f4"
            ]
        ]
    },
    {
        "id": "1b444420.62923c",
        "type": "function",
        "z": "4ab9fe27.86f9c8",
        "name": "IDを退避",
        "func": "msg.process =  {\n    \"_id\" : msg.payload\n};\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 280,
        "y": 140,
        "wires": [
            [
                "9c5b58f8.0a78d"
            ]
        ]
    },
    {
        "id": "7707921c.c4755c",
        "type": "debug",
        "z": "4ab9fe27.86f9c8",
        "name": "",
        "active": true,
        "console": "false",
        "complete": "true",
        "x": 690,
        "y": 260,
        "wires": []
    },
    {
        "id": "26d24ca.e909734",
        "type": "json",
        "z": "4ab9fe27.86f9c8",
        "name": "",
        "pretty": true,
        "x": 290,
        "y": 260,
        "wires": [
            [
                "efda5601.e13fe8",
                "947eff54.91dc6"
            ]
        ]
    },
    {
        "id": "34bb144a.7dbd0c",
        "type": "comment",
        "z": "4ab9fe27.86f9c8",
        "name": "Flow定義をbase64変換",
        "info": "",
        "x": 340,
        "y": 300,
        "wires": []
    },
    {
        "id": "ce0f378e.d9db9",
        "type": "comment",
        "z": "4ab9fe27.86f9c8",
        "name": "ファイル更新",
        "info": "",
        "x": 490,
        "y": 360,
        "wires": []
    },
    {
        "id": "3d9fa74c.87c2f8",
        "type": "comment",
        "z": "4ab9fe27.86f9c8",
        "name": "ファイル新規登録",
        "info": "",
        "x": 790,
        "y": 500,
        "wires": []
    },
    {
        "id": "dfe5779d.fb3b28",
        "type": "comment",
        "z": "4ab9fe27.86f9c8",
        "name": "まだファイルがなく失敗した場合",
        "info": "",
        "x": 250,
        "y": 480,
        "wires": []
    },
    {
        "id": "efda5601.e13fe8",
        "type": "debug",
        "z": "4ab9fe27.86f9c8",
        "name": "",
        "active": true,
        "console": "false",
        "complete": "false",
        "x": 450,
        "y": 220,
        "wires": []
    },
    {
        "id": "f4070782.4803a8",
        "type": "debug",
        "z": "4ab9fe27.86f9c8",
        "name": "",
        "active": true,
        "console": "false",
        "complete": "true",
        "x": 510,
        "y": 440,
        "wires": []
    },
    {
        "id": "5055bcc.3113cc4",
        "type": "function",
        "z": "4ab9fe27.86f9c8",
        "name": "Git PUSH MSG作成",
        "func": "msg.payload = msg.inputMessage.payload;\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 580,
        "y": 540,
        "wires": [
            [
                "7dde70e9.f74138"
            ]
        ]
    },
    {
        "id": "166ec69b.fef401",
        "type": "websocket-listener",
        "z": "",
        "path": "/ws/push",
        "wholemsg": "false"
    },
    {
        "id": "fa916f31.d3a12",
        "type": "gitlab-config",
        "z": "",
        "url": "https://git.ng.bluemix.net/api/v3",
        "project_id": "11111",
        "name": "TEST"
    }
]

これで実装は完了です。

使ってみる

試しに動かしてみます。今回は(別の環境作ってないので)今回作成したNode-REDのフローの変更を検知しGitにPushさせる形で試します。

  • Node-RED上で「デプロイ」ボタンを押します。そうすると、まずはFanctionsが走ります。Functionsでは以下のようなログが確認できます。 スクリーンショット 2017-12-24 0.52.41.png

その後、Node-RED側に処理がわたり、Gitに(初回は)新規ファイルが追加されます。
スクリーンショット 2017-12-24 0.38.14.png

  • 試しに更新してみます。フローを少し変更し、「デプロイ」を押します。そうするとファイルの更新が行われ、Git上ではdiffも確認することができます。 スクリーンショット 2017-12-24 0.42.11.png

まとめ

いかがでしたでしょうか。これでFlow更新時のバージョン管理が自動的に行われるようになりますので、Node-REDで複数人でフロー開発する際も安心して進められるのでは、と思います!

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
8