owned this note
owned this note
Published
Linked with GitHub
# Terraform/AWS Provier source code reading #1
https://terraform-jp.connpass.com/event/135906/
とはいえーいきなり集まってさぁはい、だとあれなので事前にあれこれしとくメモ
## Repository
cloneして遊びまわれるようにしておくこと
- 本体:https://github.com/hashicorp/terraform
- AWS Provider: https://github.com/terraform-providers/terraform-provider-aws
## 疑問
- Terraformはどうやってproviderを呼んでいるのか
- terraformのcore
- hashicorp/terraformのリポジトリ
- v10でproviderが分割された
- .tfをparseしてproviderを確認
- providerを探してなければDL
- providerのバイナリを別プロセスで起動
- パット見わからない
- source code読めばわかる
- その間でrpcの通信
- v12からgrpc
- main.goは他を呼んでるだけ、中身は割と薄い
- realMainやwrappedMainが中にはある
- なぜWrapするのか、本処理を子プロセスにすることで親側でエラーを拾って制御している?
- providerがAWSにどのように命令を行っているのか(api?)
- AWS sdkに対して命令を出している / sdk, api
- providerのCI/CDでは何をテストしているのか
- dependsをどう組み立てているかが読めない
- provider側、depends_onしてなくてもsecurty group先に作ってec2作ったりするところ
- resourceの順番coreの中, graphを作っている
- resourceを作るかはprovider
- どこから読むのが良いか(最初はやっぱりmain.go?)
- main.goからprobider-awsまで読んで行くのはなかなか重い、なぜなら初期化のプロセスが結構あるから
- TRACEオプションを入れてterraform planなど流すのがわかりやすいかも、minamijoyoさんの手順だとさらに行数までは出るので良さ
-
## 得た知見
* Q. なぜ wrapped と 非 wrapped で分岐している?
* => fork のコードを書こうとするとこういうコードをよく見る。
* provider と core は別プロセスで動いている
* テストを実行すると実際にAWSアカウントにリソースが作られる => テストによっては課金がとんでもないことに...
* テストするリソースを絞る方法
* ```make testacc TEST=./aws TESTARGS='-run=TestAccAWSCloudwatchLogSubscriptionFilter_'```
* SGの1ルールはAWS内ではIDなど付与されておらず、Terraformはそれを区別する必要がありIP、Cidr、ポートなどからハッシュ値を生成して区別するらしい
* わかりやすい修正、snapshot_idが3分割されてしまう対策
* https://github.com/terraform-providers/terraform-provider-aws/pull/9146/files
## 動かす
sandbox環境的なのがほしいなあ。どうするかなあ。
なんかみんなでコード書けて、それがapplyしたり消したりできる何かがほしいなあ
バージョンも切り換えたりできたりして。
### 動かしながらデバッグするのに便利情報 by minamijoyo氏
[Slackのここからの流れを勝手にメモ](https://terraform-jp.slack.com/archives/CKM6XUP4Y/p1561473636084600)
minamijoyo氏に感謝しつつ勝手にメモります。
Mac前提ですが、0からこれやればとりあえず手元でTerraform動かせる様になります。(私はなりました)
#### Go言語環境の整え
golangインストール(バージョンはTerraformの[v1.12.4](https://github.com/hashicorp/terraform/blob/v0.12.3/.go-version)を推奨
```bash
brew install goenv
goenv install 1.12.4
goenv global 1.12.4
```
(余談)goenvで1.12beta1しか選択できない場合は以下をすると治るかも
```bash
brew unlink goenv
brew install --HEAD goenv
```
以下を確認
```bash
$ go version
go version go1.12.4 darwin/amd64
or
go version go1.12.6 darwin/amd64 <-多分こっちになる
```
GOPATHとかは好きに設定してよいです。GOPATHがなにか分からない人は適当にぐぐって下さい。
ざっくりいうとGoのソースコードを置くルートディレクトリで、基本的にGOPATHの下で作業する(GOPATHの外でも作業できるけど、話がややこしくなるのでgo.modの話は割愛
Go初心者向けの説明が雑なので誰かわかる人フォローして下さい
minamijoyo氏のgo関連設定は以下。(GOPATHを$HOMEにするのは必須ではないです)
```bash
# goの設定
export GOPATH=$HOME
export PATH=$PATH:$GOPATH/bin
export GOENV_DISABLE_GOPATH=1
eval "$(goenv init -)"
```
morihaya余談、gomodはこのブログ&資料が良かったです。
[Go Modulesの概要とGo1.12に含まれるModulesに関する変更 #golangjp #go112party](https://budougumi0617.github.io/2019/02/15/go-modules-on-go112/)
[ghq](https://github.com/motemen/ghq)と$GOPATH/src合わせておくと捗どるのでおすすめです
>ぼくもgopath/src以下にgit repository全部いれてます、便利
>ghq -> pecoでシュッシュ移動できる
#### terraformをビルドしてみる
terraform のコードを手元にcloneしてきましょう
```bash
git clone https://github.com/hashicorp/terraform
cd terraform
```
準備できたらとりあえずコードいじらずに `make dev` で一旦バイナリビルドできるか試す。
*Go Modulesの関連で `GO111MODULE=on` が必要
```bash
git checkout v0.12.3
export GO111MODULE=on
make dev
```
以下の様に出力されます。
```bash
[terraform@:2e8cb7218|✔]$ make dev
==> Checking that code complies with gofmt requirements...
GO111MODULE=off go get -u golang.org/x/tools/cmd/stringer
GO111MODULE=off go get -u golang.org/x/tools/cmd/cover
GO111MODULE=off go get -u github.com/golang/mock/mockgen
GOFLAGS=-mod=vendor go generate ./...
2019/06/26 00:01:48 Generated command/internal_plugin_list.go
# go fmt doesn't support -mod=vendor but it still wants to populate the
# module cache with everything in go.mod even though formatting requires
# no dependencies, and so we're disabling modules mode for this right
# now until the "go fmt" behavior is rationalized to either support the
# -mod= argument or _not_ try to install things.
GO111MODULE=off go fmt command/internal_plugin_list.go > /dev/null
go install -mod=vendor .
```
ビルドされたバイナリは `$GOPATH/bin/terraform` にできます。
```bash
[terraform@:2e8cb7218|✔]$ $GOPATH/bin/terraform version
Terraform v0.12.3
```
#### terraform-provider-awsビルドしてみる
リポジトリを取得する
```bash
$ git clone https://github.com/terraform-providers/terraform-provider-aws
$ cd terraform-provider-aws
```
ビルド
```bash
git checkout v2.16.0
export GO111MODULE=on
make
```
`export GO111MODULE=on` だと 以下じゃないと動きませんでした。bzr入れれば良いのかも
```bash
export GO111MODULE=off
make
```
エラー内容
```
==> Checking that code complies with gofmt requirements...
go install
go: labix.org/v2/mgo@v0.0.0-20140701140051-000000000287: bzr branch --use-existing-dir https://launchpad.net/mgo/v2 . in /usr/local/go/pkg/mod/cache/vcs/ca61c737a32b1e09a0919e15375f9c2b6aa09860cc097f1333b3c3d29e040ea8: exec: "bzr": executable file not found in $PATH
go: launchpad.net/gocheck@v0.0.0-20140225173054-000000000087: bzr branch --use-existing-dir https://launchpad.net/~niemeyer/gocheck/trunk . in /usr/local/go/pkg/mod/cache/vcs/f46ce2ae80d31f9b0a29099baa203e3b6d269dace4e5357a2cf74bd109e13339: exec: "bzr": executable file not found in $PATH
go: error loading module requirements
make: *** [build] Error 1
```
makeが通ることを確認後、次に適当な作業フォルダを掘ります。
```bash
$ mkdir ~/work/tmp/YYYYMMDD
$ cd ~/work/tmp/YYYYMMDD
```
providerを読み込むtfファイル作ります。
```bash
echo 'provider "aws" {}' > main.tf
```
ビルドしたバイナリを呼び出します。
```bash
TF_LOG=TRACE $GOPATH/bin/terraform init
```
以下の様に出力されます。
```
[20190626@:|✔]$ TF_LOG=TRACE $GOPATH/bin/terraform version
2019/06/26 00:39:29 [INFO] Terraform version: 0.12.3
2019/06/26 00:39:29 [INFO] Go runtime version: go1.12.4
2019/06/26 00:39:29 [INFO] CLI args: []string{"/Users/myname/bin/terraform", "version"}
2019/06/26 00:39:29 [DEBUG] Attempting to open CLI config file: /Users/myname/.terraformrc
2019/06/26 00:39:29 [DEBUG] File doesn't exist, but doesn't need to. Ignoring.
2019/06/26 00:39:29 [INFO] CLI command args: []string{"version"}
Terraform v0.12.3
2019/06/26 00:39:29 [DEBUG] checking for provider in "."
2019/06/26 00:39:29 [DEBUG] checking for provider in "/Users/myname/bin"
2019/06/26 00:39:29 [WARN] found legacy provider "terraform-provider-aws"
2019/06/26 00:39:29 [DEBUG] checking for provider in ".terraform/plugins/darwin_amd64"
2019/06/26 00:39:29 [DEBUG] checking for provider in "/Users/myname/.terraform.d/plugins"
2019/06/26 00:39:29 [DEBUG] found valid plugin: "aws", "0.0.0", "/Users/myname/bin/terraform-provider-aws"
+ provider.aws (unversioned)
```
#### デバッグしやすさのためにログオプションを変更する
##### terraform
次にterraformの本体のリポジトリに戻りまして、
ちょっとコードをいじって、勝手にログ出力オプションを変えます。
```bash
[terraform@:2e8cb7218|✔]$ git diff
diff --git a/main.go b/main.go
index c93407805..575117d2d 100644
--- a/main.go
+++ b/main.go
@@ -31,6 +31,7 @@ const (
)
func main() {
+ log.SetFlags(log.Llongfile) // ★この行を追加
// Override global prefix set by go-dynect during init()
log.SetPrefix("")
os.Exit(realMain())
```
バイナリをビルドしなおします
```bash
[terraform@:2e8cb7218|✚1]$ make dev
```
もう一度起動してみます。
```bash
[20190626@:|✔]$ TF_LOG=TRACE $GOPATH/bin/terraform version
/Users/myname/src/github.com/hashicorp/terraform/main.go:118: [INFO] Terraform version: 0.12.3
/Users/myname/src/github.com/hashicorp/terraform/main.go:121: [INFO] Go runtime version: go1.12.4
/Users/myname/src/github.com/hashicorp/terraform/main.go:122: [INFO] CLI args: []string{"/Users/myname/bin/terraform", "version"}
/Users/myname/src/github.com/hashicorp/terraform/main.go:253: [DEBUG] Attempting to open CLI config file: /Users/myname/.terraformrc
/Users/myname/src/github.com/hashicorp/terraform/main.go:264: [DEBUG] File doesn't exist, but doesn't need to. Ignoring.
/Users/myname/src/github.com/hashicorp/terraform/main.go:207: [INFO] CLI command args: []string{"version"}
Terraform v0.12.3
/Users/myname/src/github.com/hashicorp/terraform/plugin/discovery/find.go:57: [DEBUG] checking for provider in "."
/Users/myname/src/github.com/hashicorp/terraform/plugin/discovery/find.go:57: [DEBUG] checking for provider in "/Users/myname/bin"
/Users/myname/src/github.com/hashicorp/terraform/plugin/discovery/find.go:98: [WARN] found legacy provider "terraform-provider-aws"
/Users/myname/src/github.com/hashicorp/terraform/plugin/discovery/find.go:57: [DEBUG] checking for provider in ".terraform/plugins/darwin_amd64"
/Users/myname/src/github.com/hashicorp/terraform/plugin/discovery/find.go:57: [DEBUG] checking for provider in "/Users/myname/.terraform.d/plugins"
/Users/myname/src/github.com/hashicorp/terraform/command/plugins.go:234: [DEBUG] found valid plugin: "aws", "0.0.0", "/Users/myname/bin/terraform-provider-aws"
+ provider.aws (unversioned)
```
ログを出力してるファイルのフルパスとコード上の行数まで出るようになりました。
##### terraform-provider-aws
おなじ要領でterraform-provider-aws側のログオプションも変えちゃいます。
```bash
[terraform-provider-aws@:9b6d0ef61|✔]$ git diff
diff --git a/main.go b/main.go
index 4d56a352b..8a70d1169 100644
--- a/main.go
+++ b/main.go
@@ -1,11 +1,14 @@
package main
import (
"github.com/hashicorp/terraform/plugin"
"github.com/terraform-providers/terraform-provider-aws/aws"
+ "log" // ★この行を追加、エラーになるのでコメントは消すこと
)
func main() {
+ log.SetFlags(log.Llongfile) // ★この行を追加
plugin.Serve(&plugin.ServeOpts{
ProviderFunc: aws.Provider})
}
[terraform-provider-aws@:9b6d0ef61|✚1]$ make
```
##### 動作確認
バイナリがかわったので terraform initしなおします
```bash
cd ~/work/tmp/YYYYMMDD
TF_LOG=TRACE $GOPATH/bin/terraform init
```
適当なtfファイルを書いて
```hcl
provider "aws" {
region = "ap-northeast-1"
}
resource "aws_security_group" "hoge" {
name = "hoge"
egress {
from_port = 0
to_port = 0
protocol = -1
}
}
```
```bash
TF_LOG=TRACE $GOPATH/bin/terraform plan
```
terraform-provider-awsの行数が出力された行だけのgrepは以下(ただ前後の出力見えないので微妙かも
```bash
TF_LOG=TRACE $GOPATH/bin/terraform plan 2>&1 | grep -E "terraform-provider-aws[^ ]+:[0-9]+: "
```
で、変数に何が渡ってくるかが見たくなるので、原始的なprintfデバッグをします。
複雑なオブジェクトdumpするのに、適当にspewとかを勝手に入れます
[go-spew](https://github.com/davecgh/go-spew) - Implements a deep pretty printer for Go data structures to aid in debugging
```bash
$ go install github.com/davecgh/go-spew/spew
```
例えば さっきログに出てた `terraform-provider-aws/aws/config.go:298` とかに適当にprintを差し込むやろ
```bash
[terraform-provider-aws@:9b6d0ef61|✚2]$ git diff
diff --git a/aws/config.go b/aws/config.go
index 650b82767..afaa68f53 100644
--- a/aws/config.go
+++ b/aws/config.go
@@ -127,6 +127,7 @@ import (
"github.com/aws/aws-sdk-go/service/worklink"
"github.com/aws/aws-sdk-go/service/workspaces"
"github.com/aws/aws-sdk-go/service/xray"
+ "github.com/davecgh/go-spew/spew"
awsbase "github.com/hashicorp/aws-sdk-go-base"
"github.com/hashicorp/terraform/helper/logging"
"github.com/hashicorp/terraform/terraform"
@@ -296,6 +297,7 @@ func (c *Config) Client() (interface{}, error) {
}
log.Println("[INFO] Building AWS auth structure")
+ log.Printf("[TRACE] c = %s\n", spew.Sdump(c))
awsbaseConfig := &awsbase.Config{
AccessKey: c.AccessKey,
AssumeRoleARN: c.AssumeRoleARN,
```
terraform-provider-awsを再make
```bash
make
```
spewによるデバッグを試す
```bash
cd ~/work/tmp/YYYYMMDD
TF_LOG=TRACE $GOPATH/bin/terraform init
TF_LOG=TRACE $GOPATH/bin/terraform plan
```
以下の様に変数がdumpできています。 `Region: (string) (len=14) "ap-northeast-1",` とか渡ってきてるの見えます。
```txt
2019-06-26T01:12:12.034+0900 [DEBUG] plugin.terraform-provider-aws: /Users/masayuki-morita/src/github.com/terraform-providers/terraform-provider-aws/aws/provider.go:1027: [INFO] No assume_role block read from configuration
2019-06-26T01:12:12.034+0900 [DEBUG] plugin.terraform-provider-aws: /Users/masayuki-morita/src/github.com/terraform-providers/terraform-provider-aws/aws/config.go:299: [INFO] Building AWS auth structure
2019-06-26T01:12:12.034+0900 [DEBUG] plugin.terraform-provider-aws: /Users/masayuki-morita/src/github.com/terraform-providers/terraform-provider-aws/aws/config.go:300: [TRACE] c = (*aws.Config)(0xc000842000)({
2019-06-26T01:12:12.034+0900 [DEBUG] plugin.terraform-provider-aws: AccessKey: (string) "",
2019-06-26T01:12:12.034+0900 [DEBUG] plugin.terraform-provider-aws: SecretKey: (string) "",
2019-06-26T01:12:12.034+0900 [DEBUG] plugin.terraform-provider-aws: CredsFilename: (string) "",
2019-06-26T01:12:12.034+0900 [DEBUG] plugin.terraform-provider-aws: Profile: (string) "",
2019-06-26T01:12:12.034+0900 [DEBUG] plugin.terraform-provider-aws: Token: (string) "",
2019-06-26T01:12:12.034+0900 [DEBUG] plugin.terraform-provider-aws: Region: (string) (len=14) "ap-northeast-1",
2019-06-26T01:12:12.034+0900 [DEBUG] plugin.terraform-provider-aws: MaxRetries: (int) 25,
2019-06-26T01:12:12.034+0900 [DEBUG] plugin.terraform-provider-aws: AssumeRoleARN: (string) "",
2019-06-26T01:12:12.034+0900 [DEBUG] plugin.terraform-provider-aws: AssumeRoleExternalID: (string) "",
2019-06-26T01:12:12.034+0900 [DEBUG] plugin.terraform-provider-aws: AssumeRoleSessionName: (string) "",
2019-06-26T01:12:12.034+0900 [DEBUG] plugin.terraform-provider-aws: AssumeRolePolicy: (string) "",
2019-06-26T01:12:12.034+0900 [DEBUG] plugin.terraform-provider-aws: AllowedAccountIds: ([]string) <nil>,
2019-06-26T01:12:12.034+0900 [DEBUG] plugin.terraform-provider-aws: ForbiddenAccountIds: ([]string) <nil>,
2019-06-26T01:12:12.034+0900 [DEBUG] plugin.terraform-provider-aws: Endpoints: (map[string]string) {
2019-06-26T01:12:12.034+0900 [DEBUG] plugin.terraform-provider-aws: },
2019-06-26T01:12:12.034+0900 [DEBUG] plugin.terraform-provider-aws: Insecure: (bool) false,
2019-06-26T01:12:12.034+0900 [DEBUG] plugin.terraform-provider-aws: SkipCredsValidation: (bool) false,
2019-06-26T01:12:12.034+0900 [DEBUG] plugin.terraform-provider-aws: SkipGetEC2Platforms: (bool) false,
2019-06-26T01:12:12.034+0900 [DEBUG] plugin.terraform-provider-aws: SkipRegionValidation: (bool) false,
2019-06-26T01:12:12.034+0900 [DEBUG] plugin.terraform-provider-aws: SkipRequestingAccountId: (bool) false,
2019-06-26T01:12:12.034+0900 [DEBUG] plugin.terraform-provider-aws: SkipMetadataApiCheck: (bool) false,
2019-06-26T01:12:12.034+0900 [DEBUG] plugin.terraform-provider-aws: S3ForcePathStyle: (bool) false
2019-06-26T01:12:12.034+0900 [DEBUG] plugin.terraform-provider-aws: })
2019-06-26T01:12:12.034+0900 [DEBUG] plugin.terraform-provider-aws:
```
(参考)以下でdumpしたところが見つけられます
```bash
TF_LOG=TRACE $GOPATH/bin/terraform plan 2>&1 | grep -A 25 '\[TRACE\] c ='
```
## 雑多なメモ
## あると便利?
* 変数などのフラグでminamijoyoさんの行数まで出るデバッグをON/OFFできると便利かも
## どうやってContributionするのか
https://github.com/terraform-providers/terraform-provider-aws/blob/master/.github/CONTRIBUTING.md を読もう。
めっちゃ長い。
## PR読む
kteradaさんがめっちゃcontributionしてるのでいくつかピックアップしてみてみる
https://github.com/terraform-providers/terraform-provider-aws/pulls?q=is%3Apr+author%3Akterada0509+is%3Aclosed
* testを流すと実際にAWSリソースを作るので、走らせる時はお金と、環境に注意