2021年10月20日 星期三

Terraform - Optional !?

terraform 你他媽的 Optional...

The following arguments are supported:

project - (Optional) The ID of the project in which the resource belongs.
If it is not provided, the project will be parsed from the identifier of the parent resource. If no project is provided
in the parent identifier and no project is specified, the provider project is used.

然後真的塞 project 進去, 就噴:

Error: Unsupported argument

An argument named "project" is not expected here.

2021年10月18日 星期一

Terraform - googleapi: Error 409: The Cloud SQL instance already exists

今天改了 db 的 resource dependency 要整個砍掉重練看看是不是一鍵順暢建庫成功, 所以先跑了 terraform destroy (要先關掉 db instance 的 deletion protection)再跑 terraform apply 開始蓋, 結果...

googleapi: Error 409: The Cloud SQL instance already exists.

When you delete an instance, you can't reuse the name of the deleted instance until one week from the deletion date.,
instanceAlreadyExists

我以為這種鳥蛋限制只會在 Azure 上面出現.... <囧>

這年頭連 GCP 都要做誤刪資料庫甚至刪庫跑路的災難回復服務了?

這還不是最鳥的問題, 而是用 terraform apply 進行 create sql instance 等了超過 20 分鐘, 才噴這個訊息出來, 這種 instance name check 應該放在最前面檢查吧....!@#$

2021年10月4日 星期一

Terraform - the only supported value for workload pool is ...

昨天 terraform 能正常跑完, 今天換了個 project 卻噴出這個訊息:

Error: googleapi: Error 400: Currently, the only supported value for workload pool is "foobar.svc.id.goog"., badRequest

terraform 這塊的設定檔也沒改, 是怎麼噴掉的?

打開 TF_LOG 開始追 API query 看了老半天, 覺得沒有問題啊, 再仔細看看... 嗯?

"workloadIdentityConfig": {
  "identityNamespace": " foobar.svc.id.goog"
}

怎麼 foobar 前面有個疑似空白的東西, 回去翻 terraform 設定.

這邊是抓 google_project.data.name 來用, 再切到 GCP portal IAM 看看...

Permissions for project " foobar"

project name 還真的前面有個空白存在... <囧>

後來在 console 的 IAM -> Settings 把 Project name 前面的空白拿掉, 存檔, 再跑一次 terraform 就正常了.

2021年9月22日 星期三

Terraform - will be read during apply ?

有時候會在 terraform plan / apply 的時候看到這種恐怖的情況...

  # module.foobar.data.google_service_account.this will be read during apply
  # (config refers to values not yet known)
  ...
  # module.foobar.google_pubsub_subscription.this must be replaced
  ...
  # module.foobar.google_pubsub_topic.this must be replaced
  ...
  # module.foobar.google_storage_bucket.this will be destroyed
  ...
  # module.foobar.google_storage_bucket_iam_member.bucket will be destroyed
  ...
  # module.foobar.google_storage_bucket_iam_member.object will be destroyed
  ...
  # module.foobar.google_pubsub_subscription_iam_member.this will be updated in-place
  ...

在同一個層級(module.foobar), 只要有一個 data object 需要被重讀(will be read during apply)更新內容的話, 後面同級的 resource 幾乎都會被當作受到影響, 而被 terraform 進行取代(replaced)或是砍掉(destroyed)的處置, 要是這些 resource 是 pubsub topic / cloud storage bucket 這類會存資料的地方, 那就會掉資料甚至全滅.

如果直接 terraform apply -target='module.foobar' 執行更新下去, 那通常會很慘, 因為 terraform apply 列出來這些就是這次安排執行的處置, 就算 module.foobar.data.google_service_account.this 執行重讀之後內容還是一樣, 後續的取代或砍掉的處置照樣會執行, 造成誤砍誤殺的結果.

正確的手動解法, 是執行 terraform apply -target='module.foobar.data.google_service_account.this' 先把 data read 的動作獨自執行完成, 若是 data read 回來的內容不變, 那再執行 terraform plan / apply 就不會出現後續的取代或砍掉的處置. 若是內容有變, 顯示出來的可能就只是 object 裡面部分更新(will be updated in-place)的處置, 影響太大的才會被取代或是砍掉.

至於為什麼會出現需要重讀的情況, 大都是因為線上環境的設定跟 terraform 的設定不一致, 或是 gke 被更新版本導致 resource 被異動, 或者可能是 data.google_service_account.this 資料過期了, 所以需要先更新資料.

2021年6月21日 星期一

Terraform - GCP IAM apply / destroy race condition

這是一個用 terraform 處理 GCP project / pubsub subscription / pubsub topic / storage bucket / etc 的 IAM role 常會遇到的 race condition, 目前高達八成確定原因是在 GCP 的相關 API 不是 single action, 而是 atomic action, 而且設後不理沒確認是否執行完成就直接 return.

目前只能用 workaround 解法: 再執行/多執行幾次 terraform 指令. (原因後敘)

假設狀況如下: (真實狀況可能不是/不只這樣)
- bucket foobar 原本就有 role: roles/storage.legacyBucketReader
- 現在要改成 role: roles/storage.legacyBucketOwner
- 執行 terraform apply (無關的部分就省略了):
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: -/+ destroy and then create replacement Terraform will perform the following actions: # module.basement.google_storage_bucket_iam_member.bucket must be replaced -/+ resource "google_storage_bucket_iam_member" "bucket" { ~ role = "roles/storage.legacyBucketReader" -> "roles/storage.legacyBucketOwner" # forces replacement } Plan: 1 to add, 0 to change, 1 to destroy. Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value:
表示這個 bucket 的 iam role 會被先拆後建. - 輸入 yes 再按 enter 下去之後, 噴錯誤訊息出來說無法設定之類的.... (省略) - 通常只要再執行一次 terraform apply 再 yes 下去之後就可以正常執行完. - 還是噴一樣錯誤訊息的話, 那就再等一下再執行一次... - The End.

有人會問, 是不是可以用 time_sleep 的寫法讓 destroy 先執行完再 apply ?

首先, 這是一個 resource 被 replace (destroy -> apply) 的動作, 並不是分開的 resource 運作, 所以不適用上面這種方式來處理.

其次, 在 terraform 裡面這應該是個呼叫 GCP API 進行 remove 之後再 add 的行為, 中間沒有也不應該有 delay 動作影響執行效率(這也可能產生別的 race condition), 問題點是在 remove 跟 add 大概是 atomic action, 沒有全部確定執行完就 return 回來, 產生後續的 race condition 問題.

最後, 利用 time_sleep 那個寫法實在是累贅也有問題, 因為使用者通常不會知道 create 要花多久時間(create_duration), 也不會知道 destroy_duration 多久, 只能用預估或是猜的來設定. 若是 GCP 不忙的時候可能 1s 就全部跑完, 卻還要等完剩下的 29s, 或是 1m 才跑完, 設定 30s 照樣還是發生 race condition.

倒不如還是人工 delay 再人工執行同樣的 terraform 指令還比較簡單實用.

2021年5月31日 星期一

GCP - Basic.Owner is not full Owner of project

一般來說, GCP project 使用者最大的權限應該是 Basic.Owner, 幾乎所有情況都能通行無阻.

但是...

最好再加上一個 Storage Admin 的 Owner, 才不會在 Cloud Storage 之類的相關功能遇到莫名其妙的卡住. 尤其是有用 terraform 操控 bucket + iam 的時候...

如果 project 上面還有一層 organization, 最好也設定個 Organization 的 Owner 上身, 避免靈異現象...