notebook

都内でWEB系エンジニアやってます。

Cloud Workflowsでランタイム引数のデフォルト値を設定する

実行時に引数を渡すときにデフォルト値を設定したい

というよくあるパターンをWorkflowsで行えるか試してみた

今回は失敗したパターンも書きながら流れを追って書いていく

結論だけ知りたい方は最後の方を見てください

ランタイム引数の扱い

Workflowsでランタイム引数を扱う場合は次のドキュメントを読めば良い

実行リクエストでのランタイム引数渡し  |  ワークフロー  |  Google Cloud

cloud.google.com

以下実際に試してみたログも合わせて書いていく

dict.getでデフォルト値を指定する

ランタイム引数をargsとして扱う

Pythonの関数を使える部分があったので内部的にはPythonを使っているのだろう

argsに対してdict.getでデフォルト値を指定すれば簡単に設定できるのでは?

とういことで試してみたがデプロイ時にパースエラーを吐かれて終了した

  • arguments.workflow.yml
main:
  params: [args]
  steps:
    - step0:
        assign:
          - argsDict: dict(args)
    - step1:
        assign:
          - firstName: ${argsDict.get('firstName', "defaultFirstName")}
          - lastName: ${argsDict.get('lastName', "defaultLastName")}
    - step2:
        assign:
          - OutputVar: ${"Hello, " + firstName + " " + lastName + "!"}
    - step3:
        return: ${OutputVar}
ERROR: (gcloud.workflows.deploy) [INVALID_ARGUMENT] main.yaml:9:24: parse error: in workflow 'main', step 'step1': token recognition error at: '''
          - firstName: ${argsDict.get('firstName', "defaultFirstName")}
                       ^

main.yaml:9:24: parse error: in workflow 'main', step 'step1': token recognition error at: '''
          - firstName: ${argsDict.get('firstName', "defaultFirstName")}
                       ^

main.yaml:10:23: parse error: in workflow 'main', step 'step1': token recognition error at: '''
          - lastName: ${argsDict.get('lastName', "defaultLastName")}
                      ^

main.yaml:10:23: parse error: in workflow 'main', step 'step1': token recognition error at: '''
          - lastName: ${argsDict.get('lastName', "defaultLastName")}
                      ^

そもそもパース時にシングルクオートが使用できないよう

ダブルクオートにしても違うエラーでデプロイできず

ERROR: (gcloud.workflows.deploy) [INVALID_ARGUMENT] failed to build: error in step step1: error evaluating the value to assign: call target must be a symbolic reference to a sub-workflow

サブワークフローを使う

Syntax reference  |  ワークフロー  |  Google Cloud

cloud.google.com

サブワークフローを使うとデフォルト値がサポートされているのでそっちでよしなにやるのが良さそう

ドキュメントからサンプルを引っ張ってきて少し調整して試してみた

  • arguments.workflow.yml
main:
  params: [args]
  steps:
    - call_subworkflow:
        call: build_output
        args:
          first_name: ${args.first_name}
          last_name: ${args.last_name}
        result: output
    - return_message:
        return: ${output}

build_output:
  params: [first_name: "default_first_name", last_name: "default_last_name"]
  steps:
    - build_message:
        return: ${"Hello, " + first_name + " " + last_name + "!"}
$ gcloud workflows deploy argument --source=argument.workflow.yml

$ gcloud workflows run argument --data='{"first_name":"hoge"}'
error:
  context: |-
    KeyError: key not found: first_name
    in step "call_subworkflow", routine "main", line: 7
  payload: '{"message":"KeyError: key not found: first_name","tags":["KeyError","LookupError"]}'
  stackTrace:
    elements:
    - position:
        column: '23'
        length: '18'
        line: '7'
      routine: main
      step: call_subworkflow
state: FAILED

だめだった

そもそもサブワークフローに引数を渡すときにLookupしているのでその時点でエラーが発生する

サブワークフローを組み合わせて最終的にはdictを生成し直す(結論)

最後は若干パワープレイ感があるがこれで行けた

  • arguments.workflow.yml
main:
  params: [args]
  steps:
    - call_subworkflow:
        call: build_params
        args:
          arg_params: ${args}
        result: params
    - return_message:
        return: ${params}

build_params:
  params: [arg_params]
  steps:
    - fix_first_name:
        call: fix_value
        args:
          params: ${arg_params}
          key: first_name
          default: hoge
        result: fixed_first_name

    - fix_last_name:
        call: fix_value
        args:
          params: ${arg_params}
          key: last_name
          default: fuga
        result: fixed_last_name

    - build:
        assign:
          - fixed_params:
              first_name: ${fixed_first_name}
              last_name: ${fixed_last_name}

    - return_value:
        return: ${fixed_params}

fix_value:
  params: [params, key, default]
  steps:
    - check_value:
        switch:
          - condition: ${key in params}
            return: ${params[key]}
          - condition: ${not(key in params)}
            return: ${default}

こんな感じでサブワークフローを2つ用意する

  • fix_value
    • dict、key、デフォルト値を受け取って受け取ったdictに対象キーの値があるかチェックしなければデフォルト値を返すワークフロー
  • build_params
    • assignでよしなにdictの形を整えて返すワークフロー
      • デフォルト値とキーの値をデフォルト値を設定したい引数分すべて記述する
$ gcloud workflows run argument --data='{}'
result: '{"first_name":"hoge","last_name":"fuga"}'
state: SUCCEEDED

$ gcloud workflows run argument --data='{"first_name":"piyo"}'
result: '{"first_name":"piyo","last_name":"fuga"}'
state: SUCCEEDED

引数として渡したものに対しては上書きできることが確認できた

ただこの方法だとサブワークフローの中でその後使うパラメータをすべて列挙する必要があるのでデフォルト指定が必要ないものまで書く必要があるので冗長である

欲をいえばデフォルト値が必要なものだけ記述できるようにしたい

おまけ

GUIの詳細を見に行くと可視化部分ではワークフロー単位でフロー図が出力される

なので上記のワークフローだとこんな感じ

f:id:swfz:20210408200303p:plain

f:id:swfz:20210408200309p:plain

f:id:swfz:20210408200315p:plain

正直これ見ただけでイメージ湧くかというと何とも言えない…

所感

  • 無事引数のデフォルト値を指定できるようになった
  • 引数のJSONがネストしていたりするとそれに合わせて修正する必要がある
  • サブワークフローを使いこなせればわりと書きやすそうに感じた
  • 前回も書いたが式の中で配列や辞書を生成できなさそうなのがつらい
  • 前回も書いたがPythonのメソッドで使えるものリストが欲しい(どこにあるの)

前回の記事はこちら

GCPのCloud Workflowsを試す - notebook

また、今回のサンプルの定義ファイルは以下においた

swfz/cloud-workflows-samples

github.com