TT Lab
开始
学习 学习路径 课程

Terraform/OpenTofu 基础

只读的块留在了状态里

在 TT Lab 中继续学习

目标

从状态、计划和破坏这三个方面,亲自确认 data 块与 resource 有什么不同。用 OpenTofu 逐一引发并查看:读取何时发生、它不拥有什么,以及原对象不存在或发生变化时会怎样。

为什么重要

在配置文件中,data 块和 resource 一样常见,如果把两者当成同一种东西来读,事故就会悄无声息地发生。data 既不创建也不删除对象——即使执行 destroy,原对象也原封不动,所以读取并使用别的团队创建的东西的配置才是安全的。但代价是,data 不会相信状态中保存的值,而是在每次计划时重新读取。原对象一旦变化,这不是漂移,而是输入变了,下游可能被整个替换。如果要读取的对象的名称尚未确定,或者设置了 depends_on,读取就会被推迟到应用时点,这时计划中的值会整体显示为“未知”,使评审变得困难。亲身经历这四件事之后,读别人的配置时,目光就会在 data 块上停下来。

步骤

  1. 亲手在 /root/tfb-data/base/seed.txt 中创建一行 alpha,并在同一目录的 main.tf 中放置读取该文件的 data "local_file" "seed",以及把其内容写入 copy.txt 的 resource "local_file" "copy"。执行 init·apply,计划必须是干净的。
  2. 打开 /root/tfb-data/base/terraform.tfstate,分别找出 mode 为 data 的条目和 mode 为 managed 的条目的地址,在 /root/tfb-data/shape.txt 中写入 data=<그 주소> 和 managed=<그 주소>(占位符均为对应的地址)两行。地址是像 data.local_file.seed 这样包含 mode 前缀的形式。
  3. 在 /root/tfb-data/own/ 中放置样子相同的配置,但把 seed.txt 写成一行 owned。用 init·apply 创建 copy.txt 之后,运行 tofu destroy -auto-approve,并把输出保存到 /root/tfb-data/own-destroy.txt。不要再次 apply。
  4. 在 /root/tfb-data/deferred/main.tf 中放置 random_pet.name(length 2)、写入带有该名称的 made-<이름>.txt(占位符为名称)的 local_file.made、读取该文件的 data.local_file.back,以及把读到的值写入 echo.txt 的 local_file.echo。执行 init 之后,把 plan 输出保存到 /root/tfb-data/deferred-plan.txt 并 apply。
  5. 先把 /root/tfb-data/gate/ready.txt 创建为一行 ready。在同一目录的 main.tf 中放置 null_resource.prepare(triggers v = "1")、带有 depends_on = [null_resource.prepare] 的 data.local_file.ready,以及把读到的值写入 mirror.txt 的 local_file.mirror,然后执行 init。把 plan 输出保存到 /root/tfb-data/gate-plan.txt 之后再 apply。
  6. 在 /root/tfb-data/missing/main.tf 中放置读取不存在的文件 absent.txt 的 data.local_file.absent,以及导出其内容的输出 absent,然后执行 init。运行 plan,把错误输出保存到 /root/tfb-data/missing-plan.txt。命令失败也没关系。不要创建 absent.txt。
  7. 在 /root/tfb-data/tfdata/main.tf 中放置 terraform_data.note(input "v1")和导出其 output 的输出 note,然后执行 init·apply。接着把 input 改为 "v2",把 plan 输出保存到 /root/tfb-data/tfdata-plan.txt 并 apply。最后在 /root/tfb-data/mode-compare.txt 中写入 terraform_data=<tfdata 상태에서 읽은 mode> 和 local_file_seed=<base 상태에서 읽은 mode>(占位符分别为从对应状态中读取的 mode)两行。
  8. 把 /root/tfb-data/base/seed.txt 改成一行 beta,在 /root/tfb-data/base 中运行 plan,把输出保存到 /root/tfb-data/data-change.txt 并 apply。确认计划为什么会显示为替换(replace),最后计划必须重新变干净。

参考

用 data 读取已有的文件

亲手在 /root/tfb-data/base/seed.txt 中创建一行 alpha,并在同一目录的 main.tf 中放置读取该文件的 data "local_file" "seed",以及把其内容写入 copy.txt 的 resource "local_file" "copy"。执行 init·apply,计划必须是干净的。

data 块不创建,只读取。读取到的值用 data.<타입>.<이름>.<속성>(占位符依次为类型、名称、属性)引用。local provider 的 file 数据源会把文件内容放进 content 属性。

在状态中区分读取的和创建的

打开 /root/tfb-data/base/terraform.tfstate,分别找出 mode 为 data 的条目和 mode 为 managed 的条目的地址,在 /root/tfb-data/shape.txt 中写入 data=<그 주소> 和 managed=<그 주소>(占位符均为对应的地址)两行。地址是像 data.local_file.seed 这样包含 mode 前缀的形式。

状态 JSON 的每个条目都有 mode·type·name。mode 为 data 时,地址前面会带上 data.,为 managed 时则不带。与 tofu state list 的输出对照一下。

即使 destroy,只被读取的原对象也会保留

在 /root/tfb-data/own/ 中放置样子相同的配置,但把 seed.txt 写成一行 owned。用 init·apply 创建 copy.txt 之后,运行 tofu destroy -auto-approve,并把输出保存到 /root/tfb-data/own-destroy.txt。不要再次 apply。

destroy 只删除状态所拥有的东西。只用 data 读取的文件,即使状态中留有记录,也不属于所有物。destroy 之前的状态会保留为 terraform.tfstate.backup。

要读取的对象尚不存在时,读取会推迟到 apply

在 /root/tfb-data/deferred/main.tf 中放置 random_pet.name(length 2)、写入带有该名称的 made-<이름>.txt(占位符为名称)的 local_file.made、读取该文件的 data.local_file.back,以及把读到的值写入 echo.txt 的 local_file.echo。执行 init 之后,把 plan 输出保存到 /root/tfb-data/deferred-plan.txt 并 apply。

如果数据源的参数在 plan 时点是未知的值,读取就会推迟。原样阅读计划输出中那一行数据源是怎样写的。

加上 depends_on,即使文件已经存在,plan 时也读不到

先把 /root/tfb-data/gate/ready.txt 创建为一行 ready。在同一目录的 main.tf 中放置 null_resource.prepare(triggers v = "1")、带有 depends_on = [null_resource.prepare] 的 data.local_file.ready,以及把读到的值写入 mirror.txt 的 local_file.mirror,然后执行 init。把 plan 输出保存到 /root/tfb-data/gate-plan.txt 之后再 apply。

即使原对象已经存在,只要有 depends_on,工具在该依赖完成之前就不会读取。与第 1 步的计划输出对照,差别在一行中就会显现。

原对象不存在时,计划本身就会失败

在 /root/tfb-data/missing/main.tf 中放置读取不存在的文件 absent.txt 的 data.local_file.absent,以及导出其内容的输出 absent,然后执行 init。运行 plan,把错误输出保存到 /root/tfb-data/missing-plan.txt。命令失败也没关系。不要创建 absent.txt。

数据源的读取发生在计划阶段。所以读取不到时,连应用都走不到,计划就停住了。原样保存错误消息的标题和原因那一行。

terraform_data 不是 data,而是 managed

在 /root/tfb-data/tfdata/main.tf 中放置 terraform_data.note(input "v1")和导出其 output 的输出 note,然后执行 init·apply。接着把 input 改为 "v2",把 plan 输出保存到 /root/tfb-data/tfdata-plan.txt 并 apply。最后在 /root/tfb-data/mode-compare.txt 中写入 terraform_data=<tfdata 상태에서 읽은 mode> 和 local_file_seed=<base 상태에서 읽은 mode>(占位符分别为从对应状态中读取的 mode)两行。

单看名字像是数据源,但看状态中的 mode 就有了答案。计划输出中的操作词也与数据源不同——数据源是 read,而这个是有生命周期的资源。

原对象变化不是漂移,而是输入变了

把 /root/tfb-data/base/seed.txt 改成一行 beta,在 /root/tfb-data/base 中运行 plan,把输出保存到 /root/tfb-data/data-change.txt 并 apply。确认计划为什么会显示为替换(replace),最后计划必须重新变干净。

数据源不会相信状态中保存的值,而是在每次计划时重新读取。所以原对象一旦变化,捕捉到的不是状态不一致,而是下游资源的输入发生了变化。local_file 的 content 是无法原地修改的属性。