使用 Terraform External Data Source 產出一串連續日期的列表

程式技術
sharkHead

上週在寫 Terraform 的時候遇到一個問題,給定一個開始日期與結束日期,要如何在 Terraform 中產出一串由開始日期到結束日期的日期列表?

我的專案中有一個設定檔案 settings.json,其中設定了開始日期與結束日期。

{
    "start_date": "2022-09-01",
    "end_date": "2022-09-05"
}

使用 Terraform 提供的 file()jsondecode(),可以將 JSON 中的每個值,都轉成 Terraform 語言中對應的 Type。

locals {
  settings = jsondecode(file("./settings.json"))
}

之後就可以使用 local.settings 來存取 JSON 設定檔案的資料。

local.settings.start_date # 2022-09-01
local.settings.end_date # 2022-09-05

我想利用剛剛取得的開始日期與結束日期,也就是 start_dateend_date,在 Terraform 中產出一串由開始日期到結束日期的日期列表,如下方的結果。

[
    "2022-09-01",
    "2022-09-02",
    "2022-09-03",
    "2022-09-04",
    "2022-09-05"
]

翻找了一下文件,發現 Terraform 目前只提供四種時間處理方法。

formatdata():將時間戳轉成你想要的格式,提供的時間戳格式必須符合 RFC 3339

> formatdate("DD MMM YYYY hh:mm ZZZ", "2018-01-02T23:12:01Z")
02 Jan 2018 23:12 UTC

> formatdate("EEEE, DD-MMM-YY hh:mm:ss ZZZ", "2018-01-02T23:12:01Z")
Tuesday, 02-Jan-18 23:12:01 UTC

timeadd():根據提供的時間戳與想要加上的時間返回一個新的時間戳,返回的時間格式符合 RFC 3339。

> timeadd("2017-11-22T00:00:00Z", "10m")
2017-11-22T00:10:00Z

timecmp():將兩個時間做比較,若前者比後者晚則返回數字 1,相反則返回數字 -1,相等則返回 0

> timecmp("2017-11-22T00:00:00Z", "2017-11-22T00:00:00Z")
0

> timecmp("2017-11-22T00:00:00Z", "2017-11-22T01:00:00Z")
-1

> timecmp("2017-11-22T01:00:00Z", "2017-11-22T00:00:00Z")
1

timestamp():產出現在的時間戳,時間戳格式符合 RFC 3339。

> timestamp()
2018-05-13T07:44:12Z

仔細看了每個方法的內容,我感覺似乎很難用這些方法來產出一個日期陣列。問了一下公司經驗豐富的大大,他表示這部分可以使用 external data source 去做

Terraform External Data Source

根據官方文件,external data source 的方法,簡單來說就是請別人協助。我們可以將資料傳遞給其他程式語言處理,處理完之後再傳回 Terraform。

有一點需要注意的是,external data source 產出的資料需要符合 Terraform 的 map(string) 格式。

使用的程式語言可以是 Shell Script 或是 Python,這邊以 Python 當作範例。

首先新增一個 Python 程式碼檔案 date_dict_generator.py,程式碼需要傳入開始時間與結束時間參數,並產出一個日期列表。

import datetime
import json
import sys

# 讀取傳入的 JSON 參數
input = sys.stdin.read()
input_json = json.loads(input)

# 取得開始日期
start_date = datetime.datetime.strptime(
    input_json.get('start_date'), '%Y-%m-%d')
    
# 取得結束日期
end_date = datetime.datetime.strptime(
    input_json.get('end_date'), '%Y-%m-%d')

delta = datetime.timedelta(days=1)

# 宣告一個新的空 Dictionary
date_dict = {}

# 使用回圈,每跑一次開始時間 + 1 天,直到開始日期與結束日期相等為止
while start_date <= end_date:
    # 將日期當作元素的 key 與 value ,並放入剛剛宣告的 Dictionary
    date_dict[start_date.strftime('%Y%m%d')] = start_date.strftime('%Y%m%d')
    start_date += delta

json_string = json.dumps(date_dict, indent=2)

# 將日期列表以 JSON 格式印出才會回傳給 Terraform
print(json_string)

exteranl data source 的結果必須是 Terraform 的 map(string) 格式,也就是 string key 對應 string value。

文章中一開始提到的日期列表,屬於 Terraform 的 set(string) 格式,因此是不行的

[
    "2022-09-01",
    "2022-09-02",
    "2022-09-03",
    "2022-09-04",
    "2022-09-05"
]

可以稍微調整一下加上與 value 相同的 key 變成 map(string) 格式。

{
    "2022-09-01": "2022-09-01",
    "2022-09-02": "2022-09-02",
    "2022-09-03": "2022-09-03",
    "2022-09-04": "2022-09-04",
    "2022-09-05": "2022-09-05"
}

最後在 Terraform 中定義一個 external data source,並定義要執行的指令,query 參數可以傳入一個 JSON 格式的參數給外部程式使用,這傳入了開始日期與結束日期,後續就可以在 Python 程式中做計算。

data "external" "date_dict_generator" {
  program = ["python3", "${path.module}/date_dict_generator.py"]

  query = {
    start_date = local.settings.start_date
    end_date   = local.settings.end_date
  }
}

完成之後就可以使用下方的語法取得 Python 運算後的結果。

data.external.date_dict_generator.result

參考資料

sharkHead
written by
sharkHead

後端打工仔,在下班後喜歡研究各種不同的技術。稍微擅長 PHP,並偶爾涉獵前端開發。個性就像動態語言般隨興,但渴望做事能像囉嗦的靜態語言那樣嚴謹。

0 則留言