Kumogata2

連休に入って酒量が増え体重も増え、昼夜わからない生活をし、ridgepoleのRails5対応はFが減らず、必要に駆られて使い始めたElasticBeanstalkの闇が見え始めた現実から目をそらすため、以前から懸案だったCloudFormation用ツールKumogataの次世代版を作ってみました。

なにが問題だったのか

  • aws-sdk-v1
    • v1もまだまだ現役で使えるとはいえ状況を見る感じだいぶv2が主流になってきて、このままv1を使い続けるのはリスキー…なんだけどライブラリを変更するのが地味に大変。大量のFでモチベーション低下
  • モノリシック
    • Kumogataは「フォーマットコンバーター+便利ツール」というもので、この2つは論理的に切り離すことができるはずなんだけど、初期の設計に建て増しを繰り返した結果、拡張がひっじょーにしづらい状況。おまけに対応フォーマットの追加に付随してgemもばかすか追加したからインストールが重い・めんどい。さすがにtherubyracerへの依存はやりすぎだった。申し訳ない
  • Ruby DSLの黒魔術
    • Ruby DSLJSONをはくためにdslhという連想配列Ruby DSLで書くライブラリを作ったんですが、裏で結構変なことをやっているので、テンプレート全体をRubyDSLとして評価するのはリスキーな感じです。できれば「template do ... end」というブロック内だけでDSLを評価して、外部に黒魔術の影響を漏れ出さないようにすべきと考えていたけれど、すでに込み入ったソースを回収するのはちょっと大変
  • いくつかの不要そうな機能
    • パラメータの暗号化とか初期に必要そうだったので追加した謎機能がいくつかあって、たぶんほとんどのユースケースで使われておらず、コードのスパゲッティ化に貢献

Kumogata2

ということで、連休と現実逃避とビール・チューハイ20本ぐらいの結果、Kumogata2が爆誕しました。

github.com

改善点

  • aws-sdk-v2!
  • プラガブルなアーキテクチャ
  • 不要機能の削除
    • 暗号化みたいな謎機能は削除。またsshの実行なんかも普通にRubyコードとして実行できるようになった(はず)なので内部のコードは削除しました
  • ChangeSet対応
    • 待望…だけど割とびみょーな機能のChangeSetにも対応。いろいろとインターフェースを考えた結果、dry-runコマンドを実行するとChangeSetつくって、差異を出力して、ChangeSetを削除する、という動作になりました(ChangeSetを作って実行…ってだれがあの機能考えたんだろう…)

Usage

Kumogata v1とだいたい同じです。出力もだいたい同じ。

Usage: kumogata2 <command> [args] [options]

Commands:
  create         PATH_OR_URL [STACK_NAME]   Create resources as specified in the template
  update         PATH_OR_URL STACK_NAME     Update a stack as specified in the template
  delete         STACK_NAME                 Delete a specified stack
  validate       PATH_OR_URL                Validate a specified template
  list           [STACK_NAME]               List summary information for stacks
  export         STACK_NAME                 Export a template from a specified stack
  convert        PATH_OR_URL                Convert a template format
  diff           PATH_OR_URL1 PATH_OR_URL2  Compare templates logically (file, http://..., stack://...)
  dry-run        PATH_OR_URL STACK_NAME     Create a change set and show it
  show-events    STACK_NAME                 Show events for a specified stack
  show-outputs   STACK_NAME                 Show outputs for a specified stack
  show-resources STACK_NAME                 Show resources for a specified stack

Support Format:
  json, js, template

Options:
    -k, --access-key ACCESS_KEY
    -s, --secret-key SECRET_KEY
    -r, --region REGION
        --profile PROFILE
        --credentials-path PATH
        --output-format FORMAT
    -p, --parameters KEY_VALUES
    -j, --json-parameters JSON
        --[no-]deletion-policy-retain
        --[no-]disable-rollback
        --timeout-in-minutes TIMEOUT_IN_MINUTES
        --notification-arns NOTIFICATION_ARNS
        --capabilities CAPABILITIES
        --resource-types RESOURCE_TYPES
        --on-failure ON_FAILURE
        --stack-policy-body STACK_POLICY_BODY
        --stack-policy-url STACK_POLICY_URL
        --[no-]use-previous-template
        --stack-policy-during-update-body STACK_POLICY_DURING_UPDATE_BODY
        --stack-policy-during-update-url STACK_POLICY_DURING_UPDATE_URL
        --result-log PATH
        --command-result-log PATH
        --[no-]detach
        --[no-]force
        --[no-]color
        --[no-]ignore-all-space
        --[no-]debug

Ruby DSL

github.com

ほぼ一緒ですが、

  • プラグインになって本体と分離された
  • DSLを「template do ... end」内に定義
  • post処理の実行が変わった
    • _post()メソッドがなくなって、template()と同階層post()メソッドでコマンドを実行するようになります。これにより任意のRubyコマンドがたたけるので、sshなりシェルコマンドの実行なりはそこでやってください、とう感じです。
template do
  AWSTemplateFormatVersion "2010-09-09"

  Description (<<-EOS).undent
    Kumogata Sample Template
    You can use Here document!
  EOS

  Parameters do
    InstanceType do
      Default "t2.micro"
      Description "Instance Type"
      Type "String"
    end
  end

  Resources do
    myEC2Instance do
      Type "AWS::EC2::Instance"
      Properties do
        ImageId "ami-XXXXXXXX"
        InstanceType { Ref "InstanceType" }
        KeyName "your_key_name"

        UserData do
          Fn__Base64 (<<-EOS).undent
            #!/bin/bash
            yum install -y httpd
            service httpd start
          EOS
        end
      end
    end
  end
end

post do |output|
  puts output
  #=> '{"WebsiteURL"=>"http://ec2-XX-XX-XX-XX.ap-northeast-1.compute.amazonaws.com"}'
end

Plugin

parse()とdump()メソッドを定義したクラスを作れば簡単に作れます。 たとえばyamlをパースするプラグインを作ろうと思ったらlib/kumogata2/plugin/yaml.rbを含むgemをkumogata2-plugin-yamlという名前で作って、yaml.rbを以下のようにすればOK。

require 'yaml'

class Kumogata2::Plugin::YAML
  Kumogata2::Plugin.register(:yaml, ['yml'], self)

  def initialize(options)
    @options = options
  end

  def parse(str)
    YAML.load(str)
  end

  def dump(hash)
    YAML.dump(hash).colorize_as(:yaml)
    # dumpはexportやconvertで使うメソッドなので必須、という訳ではないです
    # 最悪「raise "Not implemented"」としてもいい気がします
  end

  #def post(outputs)
  #  postが定義されれば処理完了後にコールバックしてもらえます
  #end
end

CodeRayが対応していれば、いい感じに色づけしてくれます。(もちろんttyをみて適時色なしにします)。

連想配列を扱えるならhclでもperlでもpythonでもjsでもjson5でもcoffeescriptでも、論理的には対応可能なはずなので、JSONに疲れて自分の好きな言語で書きたい…というかたは新しくプラグインを書いてみるとよいのではないでしょうか?

ということで、Kumogataを細々と使っている方がいましたら、人柱バグレポやプルリクお待ちしていますm(_ _)m