24 个版本

0.1.13 2024 年 7 月 21 日
0.1.12 2023 年 12 月 11 日
0.1.5 2023 年 3 月 13 日
0.1.4 2023 年 2 月 10 日
0.0.7 2022 年 12 月 31 日

#41过程宏

Download history 32/week @ 2024-05-03 29/week @ 2024-05-10 39/week @ 2024-05-17 48/week @ 2024-05-24 45/week @ 2024-05-31 29/week @ 2024-06-07 48/week @ 2024-06-14 44/week @ 2024-06-21 20/week @ 2024-06-28 11/week @ 2024-07-05 40/week @ 2024-07-12 173/week @ 2024-07-19 59/week @ 2024-07-26 41/week @ 2024-08-02 30/week @ 2024-08-09 36/week @ 2024-08-16

181 每月下载量
17 crates 中使用

ISC 许可证

110KB
2.5K SLoC

Terrars 是一个用于在 Rust 中构建 Terraform 堆栈的工具。这是 CDK 的替代品。

查看示例helloworld.

当前状态:可用,但可能有一些粗糙的边缘和缺少的功能。我可能会继续调整以改善用户体验。

为什么使用这个或 CDK 而不是原始的 Terraform?

  • 所有的堆栈最终都会变得复杂到需要完整的编程语言来生成。CDK 和这个工具并不比原始 Terraform 更冗长,所以我总是从一开始就使用它们。
  • 在定义您的基础设施时重用代码中的常量和数据结构(JSON 结构、环境变量、端点和反向路由函数)
  • 自动补全,通过类型进行编辑时验证

为什么使用这个而不是 CDK?

  • 它是 Rust,您已经在使用
  • 更强的静态类型安全性 - CDK 在许多情况下忽略了类型,并将必需和可选参数混合在一起
  • 预生成的绑定
  • 没有复杂的类型层次结构,如作用域、继承等。
  • 更少的层 - cdk 需要 terraform,一个 cdk 命令行界面,JavaScript 工具,JavaScript 包目录,以及根据你使用的语言,还需要该语言本身。CDK 生成需要经历一个 json spec -> typescript -> generated javascript -> final language 翻译过程。 terrars 在生成和运行时都只需要 terraform,并且直接从 JSON 规范转换为 Rust。

为什么不用这个代替 CDK?

  • 你需要创建自己的工作流程。你可以创建一个简单的 build.rs 文件,但如果你想创建一个更复杂的包装器,你需要自己编写。

预生成的绑定

基础设施

其他服务

本地,软件

入门

注意:在 helloworld 中有一个完整的工作示例。

  1. terrars 和预生成的绑定(如 terrars-andrewbaxter-stripe)或自己生成(见下面的 生成)添加到你的项目中。在绑定中启用你想要使用的功能。

  2. 开发你的代码(例如:build.rs

    创建一个 Stack 并设置提供商

    let mut stack = &mut BuildStack{}.build();
    BuildProviderStripe {
        token: STRIPE_TOKEN,
    }.build(stack);
    

    默认情况下,将使用提供商类型的第一个实例来使用该提供商的资源,因此你不需要绑定它。

    然后创建资源

    let my_product = BuildProduct {
        name: "My Product".into(),
    }.build(stack);
    let my_price = BuildPrice {
        ...
    }.build(stack);
    my_price.set_product(my_product.id());
    ...
    

    最后,输出堆栈

    fs::write("mystack.tf.json", &stack.serialize("state.json")?)?;
    
  3. 在生成的 mystack.tf.json 文件所在的目录中,像往常一样调用 terraform

    Stack 还具有调用 run()get_output() 的方法来为你调用 terraform。你必须将 terraform 添加到你的路径中。)

生成绑定

虽然有些提供商有一些现成的 crate,但你也可以使用 terrars-generate 在本地为新的提供商生成代码。

  1. 使用 cargo install terrars 安装生成 CLI

  2. 创建一个配置文件。例如,要使用 hashicorp/aws,创建一个 json 文件(例如:terrars_aws.json),其中包含你想要生成的规范

    {
      "provider": "hashicorp/aws",
      "version": "4.48.0",
      "include": [
        "cognito_user_pool",
        "cognito_user_pool_client",
        "cognito_user_pool_domain",
        "cognito_user_pool_ui_customization",
        "route53_zone",
        "route53_record",
        "aws_acm_certificate",
        "aws_acm_certificate_validation"
      ],
      "dest": "src/bin/mydeploy/tfschema/aws"
    }
    

    tfschema/aws 必须是一个未使用的目录 - 当你生成代码时,它将被清除。如果 include 缺失或为空,则将生成一切(或者,你可以使用 exclude 来黑名单资源/数据源)。资源和数据源不包括提供商前缀(在这个例子中是 aws_)。数据源以 data_ 开头。

  3. 确保你的 PATH 中有 terraform。运行 cargo install terrars,然后运行 terrars-generate terrars_aws.json

  4. 第一次这样做时,创建一个 src/bin/mydeploy/tfschema/mod.rs 文件,以作为生成提供者的根。

    pub mod aws;
    

通用用法

定义

存在包含所需参数和大多数方案项(资源、栈、变量、输出等)的 Build* 结构体,并为它们提供 build 方法。如果适用,则 build 方法会在 Stack 中注册项。可以在从 build 返回的值上设置可选参数。

表达式

背景:在 Terraform 中,无论类型如何,所有字段都可以分配一个字符串模板表达式来计算栈应用期间的值。由于所有字符串都可能作为模板,非模板字符串必须转义以避免意外的插值。

terrars 如何处理:在定义资源和调用方法时,String&str 将被视为非模板字符串并适当转义。要避免转义,你可以通过 stack.str_expr(用于生成一个评估为字符串的表达式)或 stack.expr(用于其他表达式类型)来生成一个 PrimExpr 对象。要生成表达式体,你可以像往常一样使用 format!(),但请注意 - 你必须在任何用于新表达式的 PrimExpr 上调用 .raw() 以避免双重转义问题。

如果 Terraform 给你一个有关文本 _TERRARS_SENTINEL* 的错误,这意味着你可能在该值上遗漏了一个 .raw() 调用(某些表达式被双重转义)。

作为一个经验法则

  1. expression 转换到 string/field 是可以的。表达式将被转换为哨兵值并在写入模板时进行插值。
  2. string/field(没有哨兵值,如字面量等)转换到 expression 是可以的。
  3. 将包含哨兵值的 string/field 转换到 expression 是不好的。哨兵替换将发生两次,并且你会得到损坏的数据。这种情况只能发生在将表达式转换为字符串然后再转换回来时,所以不应该经常发生。

For-each

列表、集合和记录引用都有一个 .map 方法,它负责 Terraform 中的所有不同的 "for" 方法。具体来说

  • 调用 .map 并定义一个资源:执行资源级别的 for-each(由于 Terraform 的限制,这不能用于从其他资源派生的列表,因此用途非常有限,你可能只是使用 for 循环)
  • 调用 .map 并定义一个块元素:执行块级别的 for-each
  • 调用 .map 并返回一个属性引用:生成一个属性 for 表达式

.map 总是生成一个列表引用,但这也适用于设置字段。 .map_rec.map 类似,但结果是一个记录。

原始类型的 Vec 和 map

有两个辅助宏用于生成原始值的 Vec 和 map

  • primvec![v, ...] - 创建一个原始值 Vec,如果值不是原始类型,则将其转换为原始类型。用法如下:primvec!["stringone", "stringtwo"](比 vec!["stringone".into(), "stringtwo".into()] 更简单)。
  • primmap!{"k" = v, ...} - 创建一个字符串到原始值的 map,如果值不是原始类型,则将其转换为原始类型。与上述类似,执行自动转换。

它的工作原理

Terraform 提供了一种方法以 json 格式输出提供者模式。此工具使用该模式生成与 Terraform 栈文件匹配的输出结构。

表达式/模板字符串/插值/转义

以一个例子为例

format!("{}{}", my_expr, verbatim_string))

此代码需要以某种方式转义模式和对 verbatim_string,同时保留 my_expr 未转义,并且结果需要被当作“表达式”处理以防止在另一个 format! 或类似的情况下转义。这适用于不仅仅是 format!,还包括 serde 序列化(json)、其他方法。

目前 Terrars 使用一个简单的(有些脏的)技巧来避免这个问题。所有表达式都放入一个替换表中,并使用一个哨兵字符串(例如 _TERRARS_SENTINEL_99_)代替。在最终栈 json 序列化期间,字符串被转义,然后原始表达式文本被替换回来,替换哨兵文本。

这样,所有正常的字符串格式化方法都应该保留预期的表达式。

当前限制和警告

  • 并非所有 Terraform 功能都已实现

    目前我知道的缺失功能只有资源配置。

  • ignore_changes 接受字符串而不是枚举

  • 没有变量或输出静态类型检查

    我将来会添加一个 derive 宏来自动从结构生成变量/输出。

  • 非本地部署方法

    我认为这很简单,但我还没有研究。

名称

最初我叫它terrarust,但后来我觉得这个名字听起来像恐怖分子,所以我决定稳妥起见,把ut这两个字母删掉,它们代表的是虚幻竞技场(Unreal Tournament)。

依赖项

~11-22MB
~333K SLoC