テスト用パッケージをgo.modから除く(非推奨)
cronplanにそれなりにスターがついたので、go.modからテスト用パッケージを除いてみた。
以下、cronplanをv1.10.1→v1.10.4に変更したときのgo.sumの差分。
$ go get github.com/winebarrel/cronplan@v1.10.1 $ go mod tidy $ cp go.sum go.sum.bak $ go get github.com/winebarrel/cronplan@v1.10.4 $ go mod tidy $ diff -u go.sum.bak go.sum
--- go.sum.bak 2024-11-22 23:06:12 +++ go.sum 2024-11-22 23:11:45 @@ -4,15 +4,7 @@ github.com/alecthomas/participle/v2 v2.1.1/go.mod h1:Y1+hAs8DHPmc3YUFzqllV+eSQ9ljPTk0ZkPMtEdAx2c= github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/winebarrel/cronplan v1.10.1 h1:sTnmKWpGjXr3tDpgSSTCohltaimpBNXW/LgFf0SEMwo= -github.com/winebarrel/cronplan v1.10.1/go.mod h1:FXpmoZVzj9eZoyHe1lpUezcFL3Tk6p5OBSovWeHq4qY= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/winebarrel/cronplan v1.10.4 h1:lpDvd+gihqGEi7aYcvmJ1nyFVCXDULMk8XPvUQtcPyQ= +github.com/winebarrel/cronplan v1.10.4/go.mod h1:ZGEHrRZU3YNI22nDLTr9obcRMX3uBYPektCQoCmFmaY=
testify関係のパッケージがなくなっていることがわかる。
経緯
cmd/以下のCLIの依存がgo.modに入るのがいやだったので、ライブラリと関係ないものをgo.modから除外してみた(…のだが、その後調べたらcmd/以下で使われているパッケージは go get github.com/winebarrel/cronplan
をしても依存に追加されなかった)
そのついでで、テスト用のパッケージもgo.modに含まれないようになった。
方法
サブディレクトリにgo.modがあると、ルートディレクトリのgo.modにはサブディレクトリのパッケージが含まれなくなる。 なのでテスト用のソースコードはtestディレクトリに置くようにして、そこにgo.modをおいた。
ただし、そのままだとライブラリのパッケージを直接参照できないので、replaceを使って上位ディレクトリのパッケージを参照するようにした。
具体的にはtest/go.modがこんな感じ:
module github.com/winebarrel/cronplan/test go 1.21 toolchain go1.21.1 // replaceで上位ディレクトリのパッケージを参照する replace github.com/winebarrel/cronplan => ../ require ( github.com/stretchr/testify v1.9.0 // バージョン番号はダミー値 github.com/winebarrel/cronplan v0.0.0-00010101000000-000000000000 ) require ( github.com/alecthomas/participle/v2 v2.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect )
これで go get github.com/winebarrel/cronplan
してもtestifyが依存に追加されなくなる。
非推奨
以下の点であまり推奨できるやり方ではないと思っている。
- ディレクトリ構成が非標準的になる
- replaceがhacky
- テストでしか使われているパッケージがダウンロードされても実害はないような気がする
- _test.goであればビルドから除外されるし、使ってないものはリンクされないのではないだろうか?
- dependabotが少しうっとうしくなるかもしれない
先行事例
go mod tidy
を使いたかったので、このやり方にはしなかった。
その他
増えたgo.modを再帰的に探索してほしかったので、cronplanの依存パッケージのバージョンアップをdependabotからrenovateに変えることになった。
EventBridgeの複数のcron式のスケジュールを表示するCLIを作った
https://github.com/winebarrel/cronplan?tab=readme-ov-file#cronskd-cli
cronの時間の隙間を探すことがよくあるので…
$ cat exprs.txt 0 10 * * ? * 15 12 * * ? * 0 18 ? * MON-FRI * 0 8 1 * ? * 5 8-10 ? * MON-FRI * $ cronskd -s '2024-11-11' exprs.txt Mon, 11 Nov 2024 08:05:00 5 8-10 ? * MON-FRI * Mon, 11 Nov 2024 09:05:00 5 8-10 ? * MON-FRI * Mon, 11 Nov 2024 10:00:00 0 10 * * ? * Mon, 11 Nov 2024 10:05:00 5 8-10 ? * MON-FRI * Mon, 11 Nov 2024 12:15:00 15 12 * * ? * Mon, 11 Nov 2024 18:00:00 0 18 ? * MON-FRI *
メニューバーでPagerDutyのステータスがわかるmacOSアプリを作った(10ヶ月ぶり2回目)
Atlantisを使ったオペレーションの検証(あるいはPoor man's Bytebase)
BytebaseのようにSQLだけでなく任意のワンショットのコマンドの実行をAtlantisのワークフローに乗せられるのではないか…ということを思いついたので検証してみた。
まずワンショットのコマンドのterraform providerを作成。
resource "oneshot_run" "hello" { command = "echo 'hello, oneshot'" plan_command = "echo \"hello, oneshot (plan=$ONESHOT_PLAN)\"" }
terraform planでplan_command
で実行され、terraform applyでcommand
が実行される(plan_command
はapply時には実行されない)。
plan_commandのログがplan-stdout.log
plan-stderr.log
に、commandのログが stdout.log
stderr.log
に出力される。
terraformなので、一度apply(実行)されたら二度目は実行されない。
Atlantisのワークフローの設定は以下の通り。
workflows: operation: plan: steps: - init - run: echo "[WARN] Plan command was not executed" > plan-stdout.log - run: touch plan-stderr.log - run: command: terraform${ATLANTIS_TERRAFORM_VERSION} plan -input=false -refresh -out $PLANFILE output: hide - run: cat plan-stdout.log - run: cat plan-stderr.log apply: steps: - run: echo "[WARN] Apply command was not executed" > stdout.log - run: touch stderr.log - run: command: terraform${ATLANTIS_TERRAFORM_VERSION} apply $PLANFILE output: hide - run: cat stdout.log - run: cat stderr.log
GitHubのコメントが冗長なのでテンプレートを上書き。
- プロジェクトごとのapply/delete/planコマンドのコメントを削除
<details>...</details>
の折りたたみを削除
- plan_success_unwrapped.tmpl
{{ define "planSuccessUnwrapped" -}}
{{ if .EnableDiffMarkdownFormat }}{{ .DiffMarkdownFormattedTerraformOutput }}{{ else }}{{ .TerraformOutput }}{{ end }}
{{ if .PlanWasDeleted -}} This plan was not saved because one or more projects failed and automerge requires all plans pass. {{ end -}} {{ template "mergedAgain" . -}} {{ end -}}
- plan_success_wrapped.tmpl
{{ define "planSuccessWrapped" -}}
{{ if .EnableDiffMarkdownFormat }}{{ .DiffMarkdownFormattedTerraformOutput }}{{ else }}{{ .TerraformOutput }}{{ end }}
{{ if .PlanWasDeleted -}} This plan was not saved because one or more projects failed and automerge requires all plans pass. {{ end -}} {{ .PlanSummary -}} {{ template "mergedAgain" . -}} {{ end -}}
任意のコマンドを実行するPRを作成してみる。
resource "oneshot_run" "any_command" { command = <<-EOT echo 'これはapplyに実行されるコマンドの出力だよ' EOT plan_command = <<-EOT echo -e "これはplanで実行されるコマンドの出力だよ\n(plan=$ONESHOT_PLAN)" EOT }
Atlantisでplan_commandが実行されて結果がコメントとして追記される。
plan_commandを修正してpush。
diff --git a/project/hello/terraform.tf b/project/hello/terraform.tf index f1f2851..b88f327 100644 --- a/project/hello/terraform.tf +++ b/project/hello/terraform.tf @@ -27,6 +27,6 @@ resource "oneshot_run" "any_command" { echo 'これはapplyに実行されるコマンドの出力だよ' EOT plan_command = <<-EOT - echo -e "これはplanで実行されるコマンドの出力だよ\n(plan=$ONESHOT_PLAN)" + echo -e "これはplanで実行されるコマンドの出力だよ\n※修正したよ※\n(plan=$ONESHOT_PLAN)" EOT }
修正したコマンドが実行される。
atlantis apply
とコメントするとcommand
が実行される。
メモ
resource "example_thing" "example" { for_each = fileset("scripts", "**/main.sh") command = each.value plan_command = each.value }
# main.sh if [ "$ONESHOT_PLAN" = "1" ] ; then # (planの処理) else # (applyの処理) fi
- atlantis planで検証用のplan_commandが実行されるのは悪くない、ような気がする
- 任意のコマンドを実行できるのでセキュリティまわりが大変かも
- 入出力に秘匿情報がある場合はどうしたらよいか…
- plan/apply時にコマンドではなくterraform自体の出力を出す必要があるかも知れない
AWS Secrets Managerから秘匿値を取得して環境変数を設定しコマンドを実行するツールを作った
envchainのバックエンドにAWS Secrets Managerを使ったようなツールを作った。
使い方
Secrets Managerに秘匿値を設定した上で
$ aws secretsmanager get-secret-value --secret-id foo/bar { ... "SecretString": "BAZ", ... $ aws secretsmanager get-secret-value --secret-id foo/zoo # JSON secret { ... "SecretString": "{\"TOKEN\":\"AAA\",\"SECRET\":\"BBB\"}", ...
sevの設定ファル(~/.sev.toml
)を作成し
[hello] BAR = "secretsmanager://foo/bar" ZOO = "secretsmanager://foo/zoo:TOKEN" BAZ = "BAZBAZBAZ" [world] HOGE = "secretsmanager://foo/zoo:SECRET" FOGA = "secretsmanager://foo/bar" PIYO = "PIYOPIYOPIYO"
sevでラップしてプロファイル名を指定してコマンドを実行すると、秘匿値が環境変数経由でコマンドに渡される。
$ sev hello -- env FOO=BAZ ZOO=AAA BAZ=BAZBAZBAZ $ sev world -- env HOGE=BBB FUGA=BAZ PIYO=PIYOPIYOPIYO
設定ファイルにAWS_PROFILEを指定しておくと、デフォルトではそのAWS_PROFILEを使ってSecrets Manager APIを呼び出す。
[hello] AWS_PROFILE = "my-profile" BAR = "secretsmanager://foo/bar" # foo/barの値の取得にmy-profileが使われる