terraform-provider-aws 生成式验收测试Generated Acceptance Tests完整指南从注解配置到模板引擎【免费下载链接】terraform-provider-awsThe AWS Provider enables Terraform to manage AWS resources.项目地址: https://gitcode.com/GitHub_Trending/te/terraform-provider-aws本指南以 terraform-provider-aws 仓库中 docs/acc-test-generation.md 为核心系统讲解该 Provider 如何基于统一模式自动生成资源标签Resource Tagging与资源标识Resource Identity两大类验收测试。你将掌握如何通过generate.go与源码注解启用/禁用测试生成、如何用Testing(...)系列注解精细控制 PreCheck、Import、序列化与 Terraform 变量以及如何编写.gtpl配置模板与使用预定义配置段让一次配置即可批量产出高质量、可持续维护的验收测试。生成式验收测试的工作原理在 terraform-provider-aws 中资源标签测试和资源标识测试遵循同一套通用模式进行生成。其基本思路是在服务级别service level通过服务目录下的generate.go文件开启测试生成在资源/数据源级别通过源码文件上的Testing(...)等注解annotation控制是否生成、以及如何生成由位于 internal/generate/tagstests/main.go 和 internal/generate/identitytests/main.go 的生成器读取注解渲染 Go 测试文件name_tags_gen_test.go、name_identity_gen_test.go与 Terraform 配置main_gen.tf。从源码看两个生成器的流程高度一致以 tagstests/main.go 为例先通过data.ReadAllServiceData()读取服务注册数据再以GOPACKAGE环境变量定位当前服务包随后用common.ScanDirectory(.)扫描服务目录下所有 Go 源文件利用go/ast解析工厂函数factory function上的注释注解tagstests/main.go。也就是说注解实际上是写在资源/数据源工厂函数的文档注释中的生成器只关注不带接收者Recv nil且带有Doc注释的函数。生成器会进一步区分资源与数据源数据源测试会复用对应资源的配置模板并通过DataSourceResourceImplementation关联到资源的实现tagstests/main.go。在服务级启用测试生成在服务的generate.go文件中添加如下指令以启用资源标签测试生成//go:generate go run ../../generate/tagstests/main.go添加如下指令以启用资源标识测试生成//go:generate go run ../../generate/identitytests/main.go生成器会为每个被标记的资源/数据源生成以_tags_gen_test.go或_identity_gen_test.go结尾的测试文件对应源码位置见 tagstests/main.go 与 identitytests/main.go并在testdata目录下生成对应的 Terraform 配置文件。在资源级禁用测试生成当某个服务启用了测试生成后仍可针对特定资源或数据源类型单独关闭关闭资源标签测试在其源码文件中添加注解Testing(tagsTestfalse)关闭资源标识测试在其源码文件中添加注解Testing(identityTestfalse)需要说明的是Testing(tagsTestfalse)只能用于本身不支持标签的资源生成器中称之为非透明标签场景如果资源本就不支持标签却标了tagsTestfalse生成器会直接报错见 tagstests/main.go。同理identityTest注解不允许出现在数据源上identitytests/main.go。配置生成的测试注解与引用格式生成的验收测试需要通过资源类型注解进行配置。部分注解同时作用于资源标签测试与资源标识测试另一些则只作用于其中之一。引用函数与变量Reference 格式部分测试注解允许引用当前包或其它包中的函数或变量统一使用如下格式[package path;[package alias;]]function name例如引用其它包中的函数时可写为github.com/hashicorp/terraform-plugin-testing/helper/acctest;sdkacctest;sdkacctest.RandString(5)其中包路径、别名、函数名之间用分号分隔别名可省略。注解的解析实现在 internal/generate/tests/annotations.go例如preCheck、generator、existsType等注解都会调用common.ParseIdentifierSpec解析这种引用并自动补全 Go import。公共配置适用于两类测试以下配置同时适用于资源标签测试与资源标识测试。PreCheck 函数资源类型的既有测试在PreCheck函数中通常包含一个或多个检查函数。所有生成的验收测试都会包含标准的acctest.PreCheck。多数情况下验收测试还需要额外的 PreCheck 函数限定可用区域若测试只能在特定区域运行可使用acctest.PreCheckRegion通过注解Testing(preCheckRegionregions)指定regions为用分号分隔的一个或多个区域名。解析时会将区域名转换为endpoints.RegionRegionID常量并引入github.com/hashicorp/aws-sdk-go-base/v2/endpoints包annotations.go。标准 PreCheck大多数 PreCheck 函数的签名为func(ctx context.Context, t *testing.T)用Testing(preCheckreference)指定其中reference使用上文引用格式。允许多个Testing(preCheck)注解生成器会按顺序追加到 PreCheck 代码块列表annotations.go。带区域的 PreCheck部分 PreCheck 函数的签名为func(ctx context.Context, t *testing.T, region string)用Testing(preCheckWithRegionreference)指定同样允许多个。必需的环境变量若测试需要在某个环境变量存在但值未被使用时才运行用Testing(requireEnvVarname)可多次使用。若环境变量的值会被测试使用用Testing(requireEnvVarValuename)。该注解会向生成的测试配置中添加一个与变量名同名的 Terraform 变量。可多次使用。备用初始化 Provider某些资源类型需要为备用 AWS 账号或备用区域配置额外的 Provider 实例。需要提醒的是如果测试涉及多区域应优先考虑直接给备用区域的资源使用region属性而不是单独创建一个 Provider 实例。为备用账号配置 Provider 实例使用Testing(useAlternateAccounttrue)。生成器会额外加入 PreCheck 函数acctest.PreCheckAlternateAccount并初始化一个别名为awsalternate的 Provider 实例annotations.go。为备用区域配置 Provider 实例使用Testing(altRegionProvidertrue)。生成器会额外加入 PreCheck 函数acctest.PreCheckMultipleRegion并初始化一个别名为awsalternate的 Provider 实例annotations.go。Exists 与 Destroy 检查部分Exists函数接收返回 API 对象的指针。用Testing(existsTypereference)指定参数类型格式为package path;[package alias;]function call。例如 S3 Object 资源// Testing(existsTypegithub.com/aws/aws-sdk-go-v2/service/s3;s3.GetObjectOutput)极少数资源类型没有Exists函数用Testing(hasExistsFunctionfalse)指定默认值为true见 annotations.go 中的初始化。一些较老的资源类型使用不接收testing.T参数的Exists与DestroyCheck变体此时分别添加Testing(existsTakesTfalse)和Testing(destroyTakesTfalse)。生成器中ExistsTakesT()/DestroyTakesT()即由此计算annotations.go。部分资源类型使用无操作CheckDestroy函数acctest.CheckDestroyNoop用Testing(checkDestroyNooptrue)指定生成器会自动引入internal/acctest包annotations.go。Import 状态测试步骤生成的验收测试包含ImportState步骤多数情况下可直接工作导入时需要忽略某些参数值Testing(importIgnore...)值为用分号;分隔的参数名列表。注意一旦设置了importIgnore默认的 plan 行为会被假定为更新资源importActionUpdate见 annotations.go。资源类型不支持 Import 操作使用NoImport注解。覆盖导入 ID 的多种方式使用已有变量的值Testing(importStateIdvar name)从某个资源属性获取标识符Testing(importStateIdAttributeattribute name)通过resource.ImportStateIdFunc获取Testing(importStateIdFuncfunc name)。测试序列化若测试需要串行执行使用Testing(serializetrue)。若串行测试之间需要延迟再使用Testing(serializeDelayduration)时长格式遵循 Go 的time.ParseDuration()例如 3 分 30 秒写为3m30s。从源码还可看到Testing(serializeParallelTeststrue)注解annotations.go用于控制并行测试的序列化行为。Terraform 变量生成器与附加变量大部分测试配置只接收单个参数通常是一个名称或域名。最常见的是参数rName其值由acctest.RandomWithPrefix(t, acctest.ResourcePrefix)生成——这正是默认行为只要没有显式指定generator生成器就会自动注入该生成器并引入internal/acctest包annotations.go。若不需要rName添加Testing(generatorfalse)。注意如果生成的 Terraform 配置没有引用生成的rName变量tflint自动化检查会以terraform_unused_declarations错误失败因此要么配置引用它要么显式关闭生成器。使用其它值将generator设置为某个函数调用的引用格式为[package path;[package alias;]]function call。例如 Service Catalog Portfolio 使用一个 5 字符随机字符串// Testing(generatorgithub.com/hashicorp/terraform-plugin-testing/helper/acctest;sdkacctest;sdkacctest.RandString(5))TLS 密钥与证书设置Testing(tlsKeytrue)会向配置中加入 Terraform 变量certificate_pem与private_key_pem。默认证书通用名common name为example.com可用Testing(tlsKeyDomainreference)引用已有变量覆盖。例如 API Gateway v2 Domain Name 将rName设为acctest.RandomSubdomain()并用Testing(tlsKeyDomainrName)引用。从源码可见其实现生成器会调用acctest.TLSRSAPrivateKeyPEM(t, 2048)生成 RSA 私钥再用acctest.TLSRSAX509SelfSignedCertificatePEM基于该私钥与指定 CN 生成自签名证书tagstests/main.go。SSH 公钥设置Testing(sshKeyPairtrue)Terraform 变量名为public_key底层使用sdkacctest.RandSSHKeyPair生成annotations.go。TLS ECDSA 公钥 PEM设置Testing(tlsEcdsaPublicKeyPemtrue)Terraform 变量名为rTlsEcdsaPublicKeyPem底层通过acctest.TLSECDSAPrivateKeyPEM(t, P-384)与acctest.TLSECDSAPublicKeyPEM生成annotations.go。随机 BGP ASN网络类测试Testing(randomBsgAsnlow end;high end)其中low end与high end为随机 ASN 值的上下界Terraform 变量名为rBgpAsn底层调用acctest.RandIntRangeannotations.go。随机 IPv4 地址网络类测试Testing(randomIPv4AddressCIDR range)生成的地址落在该 CIDR 范围内Terraform 变量名为rIPv4Address底层调用sdkacctest.RandIpAddressannotations.go。ACM 证书与根域名部分测试需要与特定根域名关联的 ACM 证书根域名从环境变量ACM_CERTIFICATE_ROOT_DOMAIN读取。使用Testing(acmRootDomainTfVar)或Testing(acmRootDomainTfVarvariable name)启用默认 Terraform 变量名为rootDomain环境变量未设置时测试会被跳过底层调用acctest.ACMCertificateDomainFromEnv。也可用Testing(acmSubdomainTfVarparent variable;variable name)生成根域名的随机子域默认父变量名为rootDomain、默认子域变量名为domainName。例如Testing(acmSubdomainTfVarrootDomain;domainName)会生成rootDomain变量的随机子域实现见 annotations.go。需要强调目前无法再定义额外参数。如果某个资源需要更多参数、且无法从rName推导则该资源类型必须使用手工创建的验收测试详见 Resource Tagging 文档。Resource Identity 测试配置以下注解用于配置生成的资源标识测试。区域覆盖Region Override测试默认情况下除非资源类型是全局global的否则资源标识还会在验收测试的备用区域中测试备用区域由环境变量AWS_ALTERNATE_REGION配置、默认值为us-east-1。极少数场景例如服务只在特定区域可用可用Testing(identityRegionOverrideTestfalse)省略备用区域测试。生成器通过GenerateRegionOverrideTest()判断只有资源带有区域属性非全局或已弃用区域覆盖且HasRegionOverrideTest为真时才生成region_override配置identitytests/main.go。id与资源标识属性重复Duplicated Attributes若资源类型使用Plugin Framework实现具有 ARN Identity、Singleton Identity 或 Custom Inherent Region Identity且存在一个或多个与 ARN 属性重复的属性则在 identity 注解中添加identityDuplicateAttributesattr[;attr]参数。例如aws_ssoadmin_application具有 ARN Identity且arn属性同时被id与application_arn重复其注解为ArnIdentity(identityDuplicateAttributesid;application_arn)仓库中真实写法见 internal/service/ssoadmin/application.go。否则如果资源类型的id属性与某个标识属性值相同添加Testing(idAttrDuplicatesattribute_name)注解生成器会据此引入terraform-plugin-testing/config与tfjsonpath包见 identitytests/main.go。若资源类型使用Plugin SDK实现且具有 ARN Identity、Single Parameter Identity、Singleton Identity 或 Custom Inherent Region Identity默认情况下id属性会与标识属性一致。极少数情况下id属性的值不能用作标识属性此时在 identity 注解中添加duplicatesIdAttrfalse参数。例如aws_iam_policy_attachment的id属性是name的值但真正的标识属性是policy_arn所以注解为ArnIdentity(policy_arn, duplicatesIdAttrfalse)。组合属性值Composed Attribute Values部分与资源标识相关的属性是由其它属性值组合而成的典型的是arn很多情况下还有id。若arn属性可完全由已知属性值组合得到添加ArnFormat(format)其中format是需要精确匹配的字符串属性值位置用花括号包裹的属性名替换如{name}。Partition、Region、Account ID 会自动按需加入。例如aws_batch_job_definition的 ARN 格式为job-definition/{name}:{revision}。若资源类型有 ARN 值属性、但没有 ARN Identity在注解中添加参数attributearn-attribute-name。例如aws_appflow_flow使用name属性作为资源标识其ArnFormat注解带有attributearn。极少数资源类型非全局、但 ARN 值中不含区域添加参数globaltrue。例如aws_ssoadmin_application的 ARN 不含区域其ArnFormat注解带有globaltrue。若id属性可完全由已知属性值组合得到添加IdAttrFormat(format)格式规则同上。例如aws_iam_role_policy_attachment的 ID 格式为{role}/{policy_arn}。这些注解在生成器中分别对应ArnFormat、IdAttrFormat分支并支持attribute、global关键字参数identitytests/main.go。多标识测试用例Multiple Identity Test Cases某些资源类型在不同配置或模式下行为不同。例如aws_route_table_association底层的路由表可能是子网路由表管理子网流量或网关路由表管理 Internet 网关 / 虚拟私有网关的入站流量两者的创建与导入行为不同因此都应被测试。用Testing(identityTestCases...)列出各模式以;分隔。每个测试用例需要有独立的配置模板命名为resource file name_test case.gtpl。例如aws_route_table_association有注解Testing(identityTestCasessubnet;gateway)并配套模板vpc_route_table_association_subnet.gtpl与vpc_route_table_association_gateway.gtpl。生成器会遍历IdentityTestCases默认含basic为每个用例生成basic或case_basic配置identitytests/main.go。为已有资源类型添加 Resource Identity向已有资源类型添加 Resource Identity 时需要额外的验收测试来确保标识被正确写入资源。用Testing(preIdentityVersionversion)指定其中version是添加 Resource Identity 之前的最后一个 Provider 版本。例如aws_batch_job_definition在 6.5.0 版本添加了 Resource Identity因此注解为preIdentityVersionv6.4.0。生成器会基于该版本生成一个使用旧版 ProviderExternalProviders固定hashicorp/aws为对应版本的兼容测试配置identitytests/main.go。在新资源类型上启用 Resource Identity在新资源类型上启用 Resource Identity 时添加注解Testing(hasNoPreExistingResourcetrue)。注意preIdentityVersion与hasNoPreExistingResource二者必须提供其一否则生成器会报错one of preIdentityVersion or hasNoPreExistingResource is requiredidentitytests/main.go。添加新的 Resource Identity Schema 版本某些情况下资源类型需要更新资源标识 schema 版本这需要额外测试确保标识能正确迁移到新 schema。每个 Resource Identity schema 版本添加一条Testing(identityVersionidentity-schema-version;provider-version)注解。schema 版本从 0 开始计数。注意与preIdentityVersion不同identityVersion使用实际的Provider 版本。例如aws_sqs_queue在 Provider 6.10.0 版本引入 Resource Identity并在 6.19.0 更新了 schema其注解为// Testing(preIdentityVersionv6.9.0) // Testing(identityVersion0;v6.10.0) // Testing(identityVersion1;v6.19.0)可计划导入行为Plannable ImportTerraform 配置中使用import块可让 Import 操作成为 plan 的一部分。大多数情况下导入资源后不应产生任何变更但有些情况会导致 plan 中出现变更如果使用Testing(importIgnore)注解忽略了一些属性默认假设 plan 会更新资源。若某些属性如具有默认值的属性导致导入 plan 不更新资源添加Testing(plannableImportActionNoOp)。极少数情况下导入会导致 plan替换资源常见于包含证书或凭据等敏感信息的资源类型这种情况应当避免。如果正在为已有资源类型添加 Resource Identity添加Testing(plannableImportActionReplace)并考虑向 Provider 的 GitHub 仓库提交 issue 报告该资源类型在导入时会被重建。创建新资源类型时应思考如何支持导入不重建若无法做到用NoImport禁用导入支持。底层实现中plannable import 行为被建模为三态枚举NoOp、Update、Replace其中未显式设置且存在importIgnore时默认为Updateannotations.go。错误用例Error CasesResource Identity 刚引入时实现中存在一些错误。以下注解仅为完整性而记录不应在新实现中使用V60SDKv2FixTesting(v60NullValuesError)Testing(v60RefreshError)这些注解标记了修复 v6.0 初始实现中错误所需的特殊处理与测试生成器中v60NullValuesError、v60RefreshError会把PreIdentityVersion强制设为v5.100.0见 identitytests/main.go。Resource Tagging 测试配置某些 AWS 服务的标签 API 或具体资源存在非标准行为以下注解用于规避这些情况。空字符串与 null 标签值某些服务不支持值为空字符串的标签Testing(skipEmptyTagstrue)。某些服务不支持值为 null 字符串的标签Testing(skipNullTagstrue)。标签更新行为部分资源类型修改标签必须重建资源Testing(tagsUpdateForceNewtrue)。至少有一种资源类型Service Catalog Provisioned Product不支持移除标签这很可能是 AWS 侧的错误用Testing(noRemoveTagstrue)作为变通。将getTagsIn的结果直接传入 Update Input 的资源类型可能存在忽略标签未在更新中被正确排除的错误若因此报错用Testing(tagsUpdateGetTagsIntrue)生成器中TagsUpdateGetTagsIn字段的注释也标注了这是对getTagsIn()直传 Update 调用 bug 的变通见 tagstests/main.go。标识属性Identifier Attributes部分测试直接从 AWS API 读取标签值。若资源类型的Tags注解未指定identifierAttribute则用Testing(tagsIdentifierAttributeattribute name)指明listTags函数应使用哪个属性值。若资源类型还需要用于listTags函数同时指定tagsResourceType注解生成器中两者分别对应overrideIdentifierAttribute与OverrideResourceType字段见 tagstests/main.go。以 ELB v2 Load Balancer 为例其真实注解为// SDKResource(aws_alb, nameLoad Balancer) // SDKResource(aws_lb, nameLoad Balancer) // Tags(identifierAttributearn) // Testing(existsTypegithub.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types;awstypes;awstypes.LoadBalancer)见 internal/service/elbv2/load_balancer.go。Terraform 配置模板.gtpl生成的验收测试使用ConfigDirectory在目录中指定测试配置一系列 Terraform.tf文件。这些配置文件由位于testdata/tmpl/name_basic.gtpl的 Go 模板生成其中name是资源类型实现文件名去掉.go扩展名。例如 ELB v2 Load Balancer 的实现文件是load_balancer.go因此模板是testdata/tmpl/load_balancer_basic.gtpl仓库中真实文件见 internal/service/elbv2/testdata/tmpl/load_balancer_basic.gtpl。为数据源生成配置为数据源测试生成配置时生成器会复用对应资源类型的配置再额外添加一个文件testdata/tmpl/name_data_source.gtpl该文件只包含数据源块并填入将其与资源关联所需的参数。例如 ELB v2 Load Balancer 的数据源模板是testdata/tmpl/load_balancer_data_source.gtpl。生成时数据源配置会以data_source模板段的形式追加到资源配置之后tagstests/main.go。模板指令region 与 tags对于配置中声明的所有资源与数据源除非类型是全局的都需要在资源声明顶部添加 Go 模板指令{{- template region }}将tags属性替换为 Go 模板指令{{- template tags . }}配置生成时该指令会被替换为对tags属性的适当赋值。tags属性应为资源或数据源定义的最后一行且标签只应作用于被测试的资源本身。一个完整的示例resource aws_service_thing test { {{- template region }} name var.rName {{- template tags . }} }在真实仓库中aws_lb的模板遵循相同约定资源声明顶部是{{- template region }}末尾是{{- template tags . }}同时通过{{ template acctest.ConfigVPCWithSubnets 2 }}引入预定义配置段见 internal/service/elbv2/testdata/tmpl/load_balancer_basic.gtpl。预定义配置段Pre-Defined Configuration Sections为简化并标准化测试用 Terraform 配置生成器提供了一批与acctest包中预定义配置函数同名的配置段。使用方式是在配置模板中加入{{ template name parameters }}例如引入接收子网数量参数的acctest.ConfigVPCWithSubnets配置段{{ template acctest.ConfigVPCWithSubnets 2 }}这些模板段的真实定义位于 internal/generate/tests/acctest.tf.gtpl以下是各配置段的说明。acctest.ConfigVPCWithSubnets接收子网数量参数。创建一个名为test的aws_vpc资源以及指定数量的名为test的aws_subnet资源acctest.tf.gtpl。acctest.ConfigSubnets接收子网数量参数。创建指定数量的名为test的aws_subnet资源acctest.tf.gtpl。acctest.ConfigVPCWithSubnetsIPv6接收子网数量参数。创建一个名为test的aws_vpc资源启用assign_generated_ipv6_cidr_block以及指定数量的名为test的aws_subnet资源acctest.tf.gtpl。acctest.ConfigSubnetsIPv6接收子网数量参数。创建指定数量的名为test的aws_subnet资源子网启用 IPv6acctest.tf.gtpl。acctest.ConfigAvailableAZsNoOptIn定义名为available的aws_availability_zones数据源列出当前区域中可用且非 opt-in 的可用区。不排除任何可用区。需要排除可用区时使用acctest.ConfigAvailableAZsNoOptInExclude或acctest.ConfigAvailableAZsNoOptInDefaultExclude。acctest.ConfigAvailableAZsNoOptInExclude定义名为available的aws_availability_zones数据源列出当前区域中可用且非 opt-in 的可用区。在名为exclude_zone_ids的 local value 中声明被排除的可用区集合。acctest.ConfigAvailableAZsNoOptInDefaultExclude定义名为available的aws_availability_zones数据源列出当前区域中可用且非 opt-in 的可用区。使用默认的排除集合usw2-az4与usgw1-az2。这也是ConfigSubnets系列配置段默认引用的可用区数据源见 acctest.tf.gtpl。acctest.ConfigLatestAmazonLinux2HVMEBSX8664AMI定义名为amzn2-ami-minimal-hvm-ebs-x86_64的aws_ami数据源返回x86_64架构的 Amazon Linux 2 实例 AMI 信息。acctest.ConfigLatestAmazonLinux2HVMEBSARM64AMI定义名为amzn2-ami-minimal-hvm-ebs-arm64的aws_ami数据源返回arm64架构的 Amazon Linux 2 实例 AMI 信息。acctest.configLatestAmazonLinux2HVMEBSAMI优先使用acctest.ConfigLatestAmazonLinux2HVMEBSARM64AMI或acctest.ConfigLatestAmazonLinux2HVMEBSX8664AMI。该配置段接收处理器架构作为参数定义名为amzn2-ami-minimal-hvm-ebs-architecture的aws_ami数据源返回指定架构的 Amazon Linux 2 实例 AMI 信息。acctest.ConfigAlternateAccountProvider仅在测试配置需要引用配置了备用账号的 AWS Provider 实例时使用。定义一个名为awsalternate的 Provider。小结terraform-provider-aws 的生成式验收测试体系将样板测试代码与资源特性声明解耦开发者在generate.go开启生成、在资源工厂函数注释中通过Testing(...)、Tags(...)、ArnIdentity(...)、ArnFormat(...)等注解声明测试需求再由 internal/generate/tagstests/main.go 与 internal/generate/identitytests/main.go 两个生成器统一渲染出 Go 测试文件与 Terraform 配置。这种模式既保证了成百上千个资源类型的验收测试风格一致、易于维护也通过preIdentityVersion、identityVersion、plannableImportAction等注解覆盖了导入、schema 迁移、多区域、多账号等复杂场景。理解注解语义与模板约定后无论是为现有资源补测试、还是为新增资源类型配置测试都可以做到一次声明、批量生成。【免费下载链接】terraform-provider-awsThe AWS Provider enables Terraform to manage AWS resources.项目地址: https://gitcode.com/GitHub_Trending/te/terraform-provider-aws创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
