Terraform可以监视目录中的更改吗?
我想监视一个文件目录,如果其中一个文件发生更改,则重新上载并运行一些其他任务。我以前的解决方案涉及监视单个文件,但这很容易出错,因为有些文件可能会被遗忘:Terraform可以监视目录中的更改吗?,terraform,Terraform,我想监视一个文件目录,如果其中一个文件发生更改,则重新上载并运行一些其他任务。我以前的解决方案涉及监视单个文件,但这很容易出错,因为有些文件可能会被遗忘: resource "null_resource" "deploy_files" { triggers = { file1 = "${sha1(file("my-dir/file1"))}" file2 = "${sha1(file("my-dir/file2"))}" file3 = "${sha1(fil
resource "null_resource" "deploy_files" {
triggers = {
file1 = "${sha1(file("my-dir/file1"))}"
file2 = "${sha1(file("my-dir/file2"))}"
file3 = "${sha1(file("my-dir/file3"))}"
# have I forgotten one?
}
# Copy files then run a remote script.
provisioner "file" { ... }
provisioner "remote-exec: { ... }
}
我的下一个解决方案是在一个资源中获取目录结构的哈希,并在第二个资源中使用此哈希作为触发器:
resource "null_resource" "watch_dir" {
triggers = {
always = "${uuid()}"
}
provisioner "local-exec" {
command = "find my-dir -type f -print0 | xargs -0 sha1sum | sha1sum > mydir-checksum"
}
}
resource "null_resource" "deploy_files" {
triggers = {
file1 = "${sha1(file("mydir-checksum"))}"
}
# Copy files then run a remote script.
provisioner "file" { ... }
provisioner "remote-exec: { ... }
}
这可以正常工作,但对mydir校验和的更改只有在第一次apply
之后才会被拾取。所以我需要应用两次,这不是很好。这有点混乱
我看不到更明显的方法来监视整个目录中的内容更改。有没有一种标准的方法可以做到这一点?Terraform似乎没有提供任何目录树遍历功能,因此我能想到的唯一解决方案是使用某种外部工具来实现这一点,如Make:
all: tf.plan
tf.plan: hash *.tf
terraform plan -o $@
hash: some/dir
find $^ -type f -exec sha1sum {} + > $@
.PHONY: all hash
然后在地形文件中:
resource "null_resource" "deploy_files" {
triggers = {
file1 = "${file("hash")}"
}
# Copy files then run a remote script.
provisioner "file" { ... }
provisioner "remote-exec: { ... }
}
我也有同样的需求,我使用资源以以下方式实现了它:
- 编写了一个脚本,使用
md5sum
#!/bin/bash
#
# This script calculates the MD5 checksum on a directory
#
# Exit if any of the intermediate steps fail
set -e
# Extract "DIRECTORY" argument from the input into
# DIRECTORY shell variables.
# jq will ensure that the values are properly quoted
# and escaped for consumption by the shell.
eval "$(jq -r '@sh "DIRECTORY=\(.directory)"')"
# Placeholder for whatever data-fetching logic your script implements
CHECKSUM=`find ${DIRECTORY} -type f | LC_ALL=C sort | xargs shasum -a 256 | awk '{ n=split ($2, tokens, /\//); print $1 " " tokens[n]} ' | shasum -a 256 | awk '{ print $1 }'`
# Safely produce a JSON object containing the result value.
# jq will ensure that the value is properly quoted
# and escaped to produce a valid JSON string.
jq -n --arg checksum "$CHECKSUM" '{"checksum":$checksum}'
- 创建
数据。外部
如下
data "external" "trigger" {
program = ["${path.module}/dirhash.sh"]
query {
directory = "${path.module}/<YOUR_DIR_PATH_TO_WATCH>"
}
}
PS:脚本依赖于jq
更新:
更新了校验和计算逻辑,以消除查找对不同平台的影响。您可以使用:
当且仅当存档的哈希已更改时,才会重新配置空资源。每当source\u dir
(在本例中为data/
)的内容更改时,将在刷新期间重建存档。我将此用于GCP中的云函数,其中云函数仅取决于文件名,因此,我需要在任何文件更改时强制更改文件名,否则会污染每次部署的云功能。我使用局部变量解决了这个问题,因此没有像archive\u文件
解决方案那样创建额外的文件。需要注意的是,您需要硬编码文件名,这在某些用例中可能是可以接受的
locals {
# Hard code a list of files in the dir
cfn_files = [
"cfn/requirements.txt",
"cfn/main.py",
]
# Get the MD5 of each file in the directory
cfn_md5sums = [for f in local.cfn_files : filemd5(f)]
# Join the MD5 sums together and take the MD5 of all of them
# Effectively checksumming the pieces of the dir you care about
cfn_dirchecksum = md5(join("-", local.cfn_md5sums))
}
...
data "archive_file" "cfn" {
type = "zip"
source_dir = "cfn"
output_path = "cfn/build/${local.cfn_dirchecksum}.zip"
}
resource "google_storage_bucket_object" "archive" {
name = data.archive_file.cfn.output_path
bucket = google_storage_bucket.cfn_bucket.name
source = data.archive_file.cfn.output_path
}
...
resource "google_cloudfunctions_function" "function" {
project = var.project_id
region = var.region
runtime = "python37"
source_archive_bucket = google_storage_bucket.cfn_bucket.name
source_archive_object = google_storage_bucket_object.archive.name
...
}
在Terraform 0.12及更高版本中,可以使用a、a和其中一个哈希函数来计算目录中文件的组合校验和:
> sha1(join("", [for f in fileset(path.cwd, "*"): filesha1(f)]))
"77e0b2785eb7405ea5b3b610c33c3aa2dccb90ea"
上面的代码将为当前目录中与名称模式匹配的每个文件计算一个sha1
校验和,将校验和合并到一个字符串中,最后为结果字符串计算一个校验和。因此,null\u资源
示例如下所示,上面的表达式作为触发器:
resource "null_resource" "deploy_files" {
triggers = {
dir_sha1 = sha1(join("", [for f in fileset("my-dir", "*"): filesha1(f)]))
}
provisioner "file" { ... }
provisioner "remote-exec: { ... }
}
请注意,fileset(“my dir”,“*”)
不考虑my dir
子目录中的文件。如果您想包含这些的校验和,请使用名称模式**
而不是*
来生成哈希,然后将其用作触发器?因为我不知道还有其他的解决办法。另外find my dir-type f-exec sha1sum+
应该更快。你是说make
作为一个外部脚本在terraform之前运行?这是一个有趣的问题,我从来没有以这种方式使用过terraform。但我想知道这是否只是Terraform没有做的事情——它管理基础设施;文件系统处理似乎是二等公民。我可能会建议您在Terraform周围使用一个包装器脚本,以根据您的需要对其进行塑造。@Joe是的。您可以让make
任务检查并生成目录的sum文件,然后将该文件内容用作触发器。谢谢。同意,这一点没有得到直接支持可能是一个暗示。但是有很多例子涉及到查看文件,而查看文件目录也没什么不同。有问题的文件是Docker Stack部署文件和支持配置,我认为它们位于Terraform区域。谢谢你的回答。我会考虑的。太棒了,非常感谢。我看到了外部资源的东西,然后我看到了JSON需求,并将其忽略。我会尝试一下这种方法。@Joe,我已经更新了校验和逻辑,以消除平台上的find
效应。请看一看。谢谢我用这个已经有一段时间了。它工作得非常好。干得好,真有意思!我认为这取决于输出压缩字节相对于输入的确定性/可重复性。这是一个偶然的结果还是拉链的稳定性被定义了?同意,我没有考虑过。幸运的是,我的实现没有向zip文件本身添加时间戳或任何东西,但即使是更新的zip文件的时间戳,也会使数据无效、更改哈希并使资源无效。如果要访问上层目录中的文件,还需要将其传递给filesha1函数。您可以使用本地文件来避免重复。例如:[对于文件集中的f(“${path.module}/./website”、“*”):filesha1(${path.module}/./website/${f}”)]如果您对tf模块的内容执行此操作,如何从该操作中排除隐藏文件/状态文件?
> sha1(join("", [for f in fileset(path.cwd, "*"): filesha1(f)]))
"77e0b2785eb7405ea5b3b610c33c3aa2dccb90ea"
resource "null_resource" "deploy_files" {
triggers = {
dir_sha1 = sha1(join("", [for f in fileset("my-dir", "*"): filesha1(f)]))
}
provisioner "file" { ... }
provisioner "remote-exec: { ... }
}