diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index ba3723f..cfc9605 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -21,6 +21,14 @@ jobs: check-latest: true - name: Install Terraform CLI uses: hashicorp/setup-terraform@v3 + - name: Setup SSH Agent + uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: ${{ secrets.SYNTASSO_KRATIX_CLI_PRIVATE_TF_MODULE_TEST_FIXTURE_REPO_DEPLOY_KEY }} + - name: Setup SSH + run: | + mkdir -p ~/.ssh + ssh-keyscan github.com >> ~/.ssh/known_hosts - name: Run make test run: make test - name: Run govulncheck @@ -55,4 +63,4 @@ jobs: release-please \ --token=$TOKEN \ --repo-url=syntasso/kratix-cli \ - release-pr \ No newline at end of file + release-pr diff --git a/cmd/init_tf_module_promise.go b/cmd/init_tf_module_promise.go index 54cee8b..817247b 100644 --- a/cmd/init_tf_module_promise.go +++ b/cmd/init_tf_module_promise.go @@ -39,7 +39,7 @@ To pull modules from private registries, ensure your system is logged in to the # Initialize a Promise from a Terraform Module in Terraform registry kratix init tf-module-promise iam \ --module-source terraform-aws-modules/iam/aws \ - --module-version 6.2.3 \ + --module-registry-version 6.2.3 \ --group syntasso.io \ --kind IAM \ --version v1alpha1`, @@ -47,7 +47,7 @@ To pull modules from private registries, ensure your system is logged in to the Args: cobra.ExactArgs(1), } - moduleSource, moduleVersion string + moduleSource, moduleRegistryVersion string ) func init() { @@ -56,7 +56,7 @@ func init() { "This can be a Git URL, Terraform registry path, or a local directory path. \n"+ "It follows the same format as the `source` argument in the Terraform module block.", ) - terraformModuleCmd.Flags().StringVarP(&moduleVersion, "module-version", "m", "", "(Optional) version of the terraform module; "+ + terraformModuleCmd.Flags().StringVarP(&moduleRegistryVersion, "module-registry-version", "r", "", "(Optional) version of the Terraform module from a registry; "+ "only use when pulling modules from Terraform registry", ) terraformModuleCmd.MarkFlagRequired("module-source") @@ -64,7 +64,13 @@ func init() { func InitFromTerraformModule(cmd *cobra.Command, args []string) error { fmt.Println("Fetching terraform module variables, this might take up to a minute...") - variables, err := internal.GetVariablesFromModule(moduleSource, moduleVersion) + + if moduleRegistryVersion != "" && !internal.IsTerraformRegistrySource(moduleSource) { + fmt.Println("Error: --module-registry-version is only valid for Terraform registry sources like 'namespace/name/provider'. For git URLs (e.g., 'git::https://github.com/org/repo.git?ref=v1.0.0') or local paths, embed the ref directly in --module-source instead.") + return fmt.Errorf("invalid use of --module-registry-version with non-registry source") + } + + variables, err := internal.GetVariablesFromModule(moduleSource, moduleRegistryVersion) if err != nil { fmt.Printf("Error: failed to download and convert terraform module to CRD: %s\n", err) return nil @@ -82,7 +88,7 @@ func InitFromTerraformModule(cmd *cobra.Command, args []string) error { return nil } - resourceConfigure, err := generateTerraformModuleResourceConfigurePipeline() + resourceConfigure, err := generateTerraformModuleResourceConfigurePipeline(moduleRegistryVersion) if err != nil { fmt.Printf("Error: failed to generate promise pipelines: %s\n", err) return nil @@ -90,8 +96,8 @@ func InitFromTerraformModule(cmd *cobra.Command, args []string) error { promiseName := args[0] extraFlags := fmt.Sprintf("--module-source %s", moduleSource) - if moduleVersion != "" { - extraFlags = fmt.Sprintf("%s --module-version %s", extraFlags, moduleVersion) + if moduleRegistryVersion != "" { + extraFlags = fmt.Sprintf("%s --module-registry-version %s", extraFlags, moduleRegistryVersion) } templateValues, err := generateTemplateValues(promiseName, "tf-module-promise", extraFlags, resourceConfigure, string(crdSchema)) if err != nil { @@ -123,7 +129,7 @@ func InitFromTerraformModule(cmd *cobra.Command, args []string) error { return nil } -func generateTerraformModuleResourceConfigurePipeline() (string, error) { +func generateTerraformModuleResourceConfigurePipeline(moduleRegistryVersion string) (string, error) { envs := []corev1.EnvVar{ { Name: "MODULE_SOURCE", @@ -131,10 +137,10 @@ func generateTerraformModuleResourceConfigurePipeline() (string, error) { }, } - if moduleVersion != "" { + if moduleRegistryVersion != "" { envs = append(envs, corev1.EnvVar{ - Name: "MODULE_VERSION", - Value: moduleVersion, + Name: "MODULE_REGISTRY_VERSION", + Value: moduleRegistryVersion, }) } @@ -150,7 +156,7 @@ func generateTerraformModuleResourceConfigurePipeline() (string, error) { "containers": []any{ v1alpha1.Container{ Name: "terraform-generate", - Image: "ghcr.io/syntasso/kratix-cli/terraform-generate:v0.2.0", + Image: "ghcr.io/syntasso/kratix-cli/terraform-generate:v0.4.0", Env: envs, }, }, diff --git a/cmd/kratix/main.go b/cmd/kratix/main.go index 70073cf..e3f97ea 100644 --- a/cmd/kratix/main.go +++ b/cmd/kratix/main.go @@ -21,7 +21,7 @@ import ( ) // needs to be updated before cutting a new release to desired version and should match the next version in .release-please-manifest.json -var version = "0.11.0" +var version = "0.12.0" func main() { cmd.Execute(version) diff --git a/internal/terraform_module.go b/internal/terraform_module.go index ed6cd4e..bb5ef8c 100644 --- a/internal/terraform_module.go +++ b/internal/terraform_module.go @@ -28,14 +28,14 @@ var ( terraformInit func(dir string) error = runTerraformInit ) -func GetVariablesFromModule(moduleSource, moduleVersion string) ([]TerraformVariable, error) { +func GetVariablesFromModule(moduleSource, moduleRegistryVersion string) ([]TerraformVariable, error) { tempDir, err := mkdirTemp("", "terraform-module") if err != nil { return nil, fmt.Errorf("failed to create temp directory: %w", err) } defer os.RemoveAll(tempDir) - if err := writeTerraformModuleConfig(tempDir, moduleSource, moduleVersion); err != nil { + if err := writeTerraformModuleConfig(tempDir, moduleSource, moduleRegistryVersion); err != nil { return nil, err } @@ -57,10 +57,10 @@ func GetVariablesFromModule(moduleSource, moduleVersion string) ([]TerraformVari return variables, nil } -func writeTerraformModuleConfig(workDir, moduleSource, moduleVersion string) error { +func writeTerraformModuleConfig(workDir, moduleSource, moduleRegistryVersion string) error { config := fmt.Sprintf("module \"%s\" {\n source = \"%s\"\n", kratixModuleName, moduleSource) - if moduleVersion != "" { - config += fmt.Sprintf(" version = \"%s\"\n", moduleVersion) + if moduleRegistryVersion != "" && IsTerraformRegistrySource(moduleSource) { + config += fmt.Sprintf(" version = \"%s\"\n", moduleRegistryVersion) } config += "}\n" if err := os.WriteFile(filepath.Join(workDir, "main.tf"), []byte(config), 0o644); err != nil { @@ -109,6 +109,21 @@ func resolveModuleDir(workDir string) (string, error) { return "", fmt.Errorf("module %s not found in terraform module manifest", kratixModuleName) } +func IsTerraformRegistrySource(moduleSource string) bool { + // Local filepaths + if strings.HasPrefix(moduleSource, "./") || strings.HasPrefix(moduleSource, "../") || strings.HasPrefix(moduleSource, "/") { + return false + } + + // URLs and other schemes + if strings.Contains(moduleSource, "://") || strings.Contains(moduleSource, "::") { + return false + } + + // Otherwise assume it's a registry source if it has at least two slashes + return strings.Count(moduleSource, "/") >= 2 +} + func extractVariablesFromVarsFile(filePath string) ([]TerraformVariable, error) { fileContent, err := readFileContent(filePath) if err != nil { diff --git a/internal/terraform_module_test.go b/internal/terraform_module_test.go index 551825d..7d1a836 100644 --- a/internal/terraform_module_test.go +++ b/internal/terraform_module_test.go @@ -132,8 +132,7 @@ variable "list_object_var" { mainContent, err := os.ReadFile(filepath.Join(tempDir, "main.tf")) Expect(err).NotTo(HaveOccurred()) expectContent := `module "kratix_target" { - source = "git::mock-source.git//subdir" - version = "v1.0.0" + source = "git::mock-source.git//subdir?ref=v1.0.0" } ` Expect(string(mainContent)).To(Equal(expectContent)) @@ -161,7 +160,7 @@ variable "bool_var" { `), 0o644) }) - variables, err := internal.GetVariablesFromModule("git::mock-source.git", "v1.0.0") + variables, err := internal.GetVariablesFromModule("git::mock-source.git//subdir?ref=v1.0.0", "") Expect(err).ToNot(HaveOccurred()) Expect(variables).To(HaveLen(4)) @@ -183,6 +182,37 @@ variable "bool_var" { }) }) + Context("when a registry module version is provided separately", func() { + BeforeEach(func() { + internal.SetTerraformInitFunc(func(dir string) error { + mainContent, err := os.ReadFile(filepath.Join(tempDir, "main.tf")) + Expect(err).NotTo(HaveOccurred()) + expectContent := `module "kratix_target" { + source = "terraform-aws-modules/iam/aws" + version = "6.2.3" +} +` + Expect(string(mainContent)).To(Equal(expectContent)) + + variablesPath := filepath.Join(tempDir, ".terraform", "modules", "kratix_target", "variables.tf") + expectManifest(filepath.Join(tempDir, ".terraform", "modules", "modules.json"), ".terraform/modules/kratix_target") + Expect(os.MkdirAll(filepath.Dir(variablesPath), 0o755)).To(Succeed()) + return os.WriteFile(variablesPath, []byte(` +variable "example_var" { + type = string +} +`), 0o644) + }) + }) + + It("adds the version to the terraform config", func() { + variables, err := internal.GetVariablesFromModule("terraform-aws-modules/iam/aws", "6.2.3") + Expect(err).ToNot(HaveOccurred()) + Expect(variables).To(HaveLen(1)) + Expect(variables[0].Name).To(Equal("example_var")) + }) + }) + Context("when terraform init fails", func() { It("errors", func() { internal.SetTerraformInitFunc(func(dir string) error { @@ -210,6 +240,20 @@ variable "bool_var" { }) }) +var _ = Describe("IsTerraformRegistrySource", func() { + DescribeTable("registry source detection", + func(source string, expected bool) { + Expect(internal.IsTerraformRegistrySource(source)).To(Equal(expected)) + }, + Entry("registry path", "namespace/name/provider", true), + Entry("nested registry path", "foo/bar/baz//bob/banana", true), + Entry("git URL", "git::https://github.com/org/repo.git?ref=v1.0.0", false), + Entry("local path", "./modules/vpc", false), + Entry("absolute path", "/tmp/module", false), + Entry("module with scheme", "https://example.com/archive.tgz", false), + ) +}) + func expectManifest(manifestPath, moduleDir string) { manifest := fmt.Sprintf(`{"Modules":[{"Key":"module.%s","Dir":"%s"}]}`, "kratix_target", moduleDir) Expect(os.MkdirAll(filepath.Dir(manifestPath), 0o755)).To(Succeed()) diff --git a/stages/terraform-module-promise/Dockerfile b/stages/terraform-module-promise/Dockerfile index 60a40ee..8394967 100644 --- a/stages/terraform-module-promise/Dockerfile +++ b/stages/terraform-module-promise/Dockerfile @@ -4,8 +4,9 @@ ARG TARGETOS WORKDIR /workspace COPY go.mod go.mod COPY go.sum go.sum -COPY stages/terraform-module-promise/main.go main.go RUN go mod download +COPY stages/terraform-module-promise/main.go main.go +COPY internal internal RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH GO111MODULE=on go build -a -o from-api-to-terraform-module main.go FROM --platform=${TARGETARCH:-$BUILDPLATFORM} gcr.io/distroless/cc:nonroot diff --git a/stages/terraform-module-promise/Makefile b/stages/terraform-module-promise/Makefile index 9f86860..f0a0549 100644 --- a/stages/terraform-module-promise/Makefile +++ b/stages/terraform-module-promise/Makefile @@ -35,4 +35,4 @@ build-and-push: # Build container image and push it to the container registry ${BASE_PATH} build-and-load: build # Build container image and load it into kind - kind load docker-image ${IMG_TAG}:${VERSION} --name platform \ No newline at end of file + kind load docker-image ${IMG_TAG}:${VERSION} --name platform diff --git a/stages/terraform-module-promise/main.go b/stages/terraform-module-promise/main.go index 6c48a0b..800afec 100644 --- a/stages/terraform-module-promise/main.go +++ b/stages/terraform-module-promise/main.go @@ -8,6 +8,7 @@ import ( "path/filepath" "strings" + "github.com/syntasso/kratix-cli/internal" "gopkg.in/yaml.v3" ) @@ -15,8 +16,11 @@ func main() { yamlFile := GetEnv("KRATIX_INPUT_FILE", "/kratix/input/object.yaml") outputDir := GetEnv("KRATIX_OUTPUT_DIR", "/kratix/output") moduleSource := MustHaveEnv("MODULE_SOURCE") - moduleVersion := MustHaveEnv("MODULE_VERSION") - modulePath := os.Getenv("MODULE_PATH") // optional + moduleRegistryVersion := os.Getenv("MODULE_REGISTRY_VERSION") + + if moduleRegistryVersion != "" && !internal.IsTerraformRegistrySource(moduleSource) { + log.Fatalf("MODULE_REGISTRY_VERSION is only valid for Terraform registry sources (e.g., \"namespace/name/provider\"). For git or local sources, embed the ref directly in MODULE_SOURCE (e.g., \"git::https://github.com/org/repo.git?ref=v1.2.3\"). Provided module_source=%q", moduleSource) + } yamlContent, err := os.ReadFile(yamlFile) if err != nil { @@ -44,19 +48,18 @@ func main() { uniqueFileName := strings.ToLower(fmt.Sprintf("%s_%s_%s", kind, namespace, name)) - source := fmt.Sprintf("%s//%s?ref=%s", moduleSource, modulePath, moduleVersion) - if modulePath == "" { - source = fmt.Sprintf("%s?ref=%s", moduleSource, moduleVersion) - } - module := map[string]map[string]map[string]any{ "module": { uniqueFileName: { - "source": source, + "source": moduleSource, }, }, } + if moduleRegistryVersion != "" { + module["module"][uniqueFileName]["version"] = moduleRegistryVersion + } + // Handle spec if it exists if spec, ok := data["spec"].(map[string]any); ok { for key, value := range spec { diff --git a/stages/terraform-module-promise/test/stage_test.go b/stages/terraform-module-promise/test/stage_test.go index 1dcfc28..c2b9a5d 100644 --- a/stages/terraform-module-promise/test/stage_test.go +++ b/stages/terraform-module-promise/test/stage_test.go @@ -50,6 +50,38 @@ var expectedOutputNoSpec = `{ } }` +var expectedRegistryOutput = `{ + "module": { + "testobject_non-default_test-object": { + "source": "terraform-aws-modules/iam/aws", + "version": "6.2.3", + "strArr": [ + { + "field": "value" + } + ], + "intArr": [ + 1 + ], + "listBool": [ + true + ], + "field": "value", + "mapWithinMap": { + "entryMap": { + "entry": "value", + "entry2": 2, + "entry3": false + }, + "entry": "value", + "entry2": 2, + "entry3": false + }, + "number": 7 + } + } +}` + func runWithEnv(envVars map[string]string) *gexec.Session { cmd := exec.Command(binaryPath) for key, value := range envVars { @@ -74,8 +106,7 @@ var _ = Describe("From TF module to Promise Stage", func() { envVars = map[string]string{ "KRATIX_INPUT_FILE": "assets/test-object.yaml", "KRATIX_OUTPUT_DIR": tmpDir, - "MODULE_SOURCE": "git::example.com", - "MODULE_VERSION": "1.0.0", + "MODULE_SOURCE": "git::example.com?ref=1.0.0", } }) @@ -104,4 +135,24 @@ var _ = Describe("From TF module to Promise Stage", func() { Expect(err).NotTo(HaveOccurred()) Expect(string(output)).To(MatchJSON(expectedOutputNoSpec)) }) + + It("adds a registry version when provided separately", func() { + envVars["MODULE_SOURCE"] = "terraform-aws-modules/iam/aws" + envVars["MODULE_REGISTRY_VERSION"] = "6.2.3" + session := runWithEnv(envVars) + Eventually(session).Should(gexec.Exit()) + Expect(session.Buffer()).To(gbytes.Say("Terraform JSON configuration written to %s/testobject_non-default_test-object.tf.json", tmpDir)) + Expect(session).To(gexec.Exit(0)) + output, err := os.ReadFile(filepath.Join(tmpDir, "testobject_non-default_test-object.tf.json")) + Expect(err).NotTo(HaveOccurred()) + Expect(string(output)).To(MatchJSON(expectedRegistryOutput)) + }) + + It("errors when registry version is used with a non-registry source", func() { + envVars["MODULE_REGISTRY_VERSION"] = "9.9.9" + session := runWithEnv(envVars) + Eventually(session).Should(gexec.Exit()) + Expect(session.ExitCode()).NotTo(Equal(0)) + Expect(session.Err).To(gbytes.Say("MODULE_REGISTRY_VERSION is only valid for Terraform registry sources")) + }) }) diff --git a/test/assets/terraform/api/git-subdir.yaml b/test/assets/terraform/api/git-subdir.yaml new file mode 100644 index 0000000..6ea5a17 --- /dev/null +++ b/test/assets/terraform/api/git-subdir.yaml @@ -0,0 +1,75 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: examples.example.com +spec: + group: example.com + names: + kind: Example + plural: examples + singular: example + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + type: object + properties: + spec: + default: {} + properties: + create: + default: true + description: Determines whether resources will be created + type: boolean + create_security_group: + default: false + description: Determines if a security group is created + type: boolean + security_group_description: + description: Description of the security group created + type: string + security_group_ids: + default: [] + description: Default security group IDs to associate with the VPC endpoints + items: + type: string + type: array + security_group_name: + description: Name to use on security group created. Conflicts with `security_group_name_prefix` + type: string + security_group_name_prefix: + description: Name prefix to use on security group created. Conflicts with `security_group_name` + type: string + security_group_tags: + additionalProperties: + type: string + default: {} + description: A map of additional tags to add to the security group created + type: object + subnet_ids: + default: [] + description: Default subnets IDs to associate with the VPC endpoints + items: + type: string + type: array + tags: + additionalProperties: + type: string + default: {} + description: A map of tags to use on all resources + type: object + timeouts: + additionalProperties: + type: string + default: {} + description: Define maximum timeout for creating, updating, and deleting VPC endpoint + resources + type: object + vpc_id: + description: The ID of the VPC in which the endpoint will be used + type: string + type: object + + served: true + storage: true diff --git a/test/assets/terraform/api/git.yaml b/test/assets/terraform/api/git.yaml new file mode 100644 index 0000000..70ebca1 --- /dev/null +++ b/test/assets/terraform/api/git.yaml @@ -0,0 +1,1357 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: examples.example.com +spec: + group: example.com + names: + kind: Example + plural: examples + singular: example + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + type: object + properties: + spec: + default: {} + properties: + amazon_side_asn: + default: "64512" + description: The Autonomous System Number (ASN) for the Amazon side of the gateway. + By default the virtual private gateway is created with the current default Amazon + ASN + type: string + azs: + default: [] + description: A list of availability zones names or ids in the region + items: + type: string + type: array + cidr: + default: 10.0.0.0/16 + description: (Optional) The IPv4 CIDR block for the VPC. CIDR can be explicitly + set or it can be derived from IPAM using `ipv4_netmask_length` & `ipv4_ipam_pool_id` + type: string + create_database_internet_gateway_route: + default: false + description: Controls if an internet gateway route for public database access + should be created + type: boolean + create_database_nat_gateway_route: + default: false + description: Controls if a nat gateway route should be created to give internet + access to the database subnets + type: boolean + create_database_subnet_group: + default: true + description: Controls if database subnet group should be created (n.b. database_subnets + must also be set) + type: boolean + create_database_subnet_route_table: + default: false + description: Controls if separate route table for database should be created + type: boolean + create_egress_only_igw: + default: true + description: Controls if an Egress Only Internet Gateway is created and its related + routes + type: boolean + create_elasticache_subnet_group: + default: true + description: Controls if elasticache subnet group should be created + type: boolean + create_elasticache_subnet_route_table: + default: false + description: Controls if separate route table for elasticache should be created + type: boolean + create_flow_log_cloudwatch_iam_role: + default: false + description: Whether to create IAM role for VPC Flow Logs + type: boolean + create_flow_log_cloudwatch_log_group: + default: false + description: Whether to create CloudWatch log group for VPC Flow Logs + type: boolean + create_igw: + default: true + description: Controls if an Internet Gateway is created for public subnets and + the related routes that connect them + type: boolean + create_redshift_subnet_group: + default: true + description: Controls if redshift subnet group should be created + type: boolean + create_redshift_subnet_route_table: + default: false + description: Controls if separate route table for redshift should be created + type: boolean + create_vpc: + default: true + description: Controls if VPC should be created (it affects almost all resources) + type: boolean + customer_gateway_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the Customer Gateway + type: object + customer_gateways: + additionalProperties: + type: object + x-kubernetes-preserve-unknown-fields: true + default: {} + description: Maps of Customer Gateway's attributes (BGP ASN and Gateway's Internet-routable + external IP address) + type: object + customer_owned_ipv4_pool: + description: The customer owned IPv4 address pool. Typically used with the `map_customer_owned_ip_on_launch` + argument. The `outpost_arn` argument must be specified when configured + type: string + database_acl_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the database subnets network ACL + type: object + database_dedicated_network_acl: + default: false + description: Whether to use dedicated network ACL (not default) and custom rules + for database subnets + type: boolean + database_inbound_acl_rules: + default: + - cidr_block: 0.0.0.0/0 + from_port: "0" + protocol: "-1" + rule_action: allow + rule_number: "100" + to_port: "0" + description: Database subnets inbound network ACL rules + items: + additionalProperties: + type: string + type: object + type: array + database_outbound_acl_rules: + default: + - cidr_block: 0.0.0.0/0 + from_port: "0" + protocol: "-1" + rule_action: allow + rule_number: "100" + to_port: "0" + description: Database subnets outbound network ACL rules + items: + additionalProperties: + type: string + type: object + type: array + database_route_table_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the database route tables + type: object + database_subnet_assign_ipv6_address_on_creation: + default: false + description: Specify true to indicate that network interfaces created in the specified + subnet should be assigned an IPv6 address. Default is `false` + type: boolean + database_subnet_enable_dns64: + default: true + description: 'Indicates whether DNS queries made to the Amazon-provided DNS Resolver + in this subnet should return synthetic IPv6 addresses for IPv4-only destinations. + Default: `true`' + type: boolean + database_subnet_enable_resource_name_dns_a_record_on_launch: + default: false + description: 'Indicates whether to respond to DNS queries for instance hostnames + with DNS A records. Default: `false`' + type: boolean + database_subnet_enable_resource_name_dns_aaaa_record_on_launch: + default: true + description: 'Indicates whether to respond to DNS queries for instance hostnames + with DNS AAAA records. Default: `true`' + type: boolean + database_subnet_group_name: + description: Name of database subnet group + type: string + database_subnet_group_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the database subnet group + type: object + database_subnet_ipv6_native: + default: false + description: 'Indicates whether to create an IPv6-only subnet. Default: `false`' + type: boolean + database_subnet_ipv6_prefixes: + default: [] + description: Assigns IPv6 database subnet id based on the Amazon provided /56 + prefix base 10 integer (0-256). Must be of equal length to the corresponding + IPv4 subnet list + items: + type: string + type: array + database_subnet_names: + default: [] + description: Explicit values to use in the Name tag on database subnets. If empty, + Name tags are generated + items: + type: string + type: array + database_subnet_private_dns_hostname_type_on_launch: + description: 'The type of hostnames to assign to instances in the subnet at launch. + For IPv6-only subnets, an instance DNS name must be based on the instance ID. + For dual-stack and IPv4-only subnets, you can specify whether DNS names use + the instance IPv4 address or the instance ID. Valid values: `ip-name`, `resource-name`' + type: string + database_subnet_suffix: + default: db + description: Suffix to append to database subnets name + type: string + database_subnet_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the database subnets + type: object + database_subnets: + default: [] + description: A list of database subnets inside the VPC + items: + type: string + type: array + default_network_acl_egress: + default: + - action: allow + cidr_block: 0.0.0.0/0 + from_port: "0" + protocol: "-1" + rule_no: "100" + to_port: "0" + - action: allow + from_port: "0" + ipv6_cidr_block: ::/0 + protocol: "-1" + rule_no: "101" + to_port: "0" + description: List of maps of egress rules to set on the Default Network ACL + items: + additionalProperties: + type: string + type: object + type: array + default_network_acl_ingress: + default: + - action: allow + cidr_block: 0.0.0.0/0 + from_port: "0" + protocol: "-1" + rule_no: "100" + to_port: "0" + - action: allow + from_port: "0" + ipv6_cidr_block: ::/0 + protocol: "-1" + rule_no: "101" + to_port: "0" + description: List of maps of ingress rules to set on the Default Network ACL + items: + additionalProperties: + type: string + type: object + type: array + default_network_acl_name: + description: Name to be used on the Default Network ACL + type: string + default_network_acl_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the Default Network ACL + type: object + default_route_table_name: + description: Name to be used on the default route table + type: string + default_route_table_propagating_vgws: + default: [] + description: List of virtual gateways for propagation + items: + type: string + type: array + default_route_table_routes: + default: [] + description: Configuration block of routes. See https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/default_route_table#route + items: + additionalProperties: + type: string + type: object + type: array + default_route_table_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the default route table + type: object + default_security_group_egress: + default: [] + description: List of maps of egress rules to set on the default security group + items: + additionalProperties: + type: string + type: object + type: array + default_security_group_ingress: + default: [] + description: List of maps of ingress rules to set on the default security group + items: + additionalProperties: + type: string + type: object + type: array + default_security_group_name: + description: Name to be used on the default security group + type: string + default_security_group_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the default security group + type: object + default_vpc_enable_dns_hostnames: + default: true + description: Should be true to enable DNS hostnames in the Default VPC + type: boolean + default_vpc_enable_dns_support: + default: true + description: Should be true to enable DNS support in the Default VPC + type: boolean + default_vpc_name: + description: Name to be used on the Default VPC + type: string + default_vpc_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the Default VPC + type: object + dhcp_options_domain_name: + default: "" + description: Specifies DNS name for DHCP options set (requires enable_dhcp_options + set to true) + type: string + dhcp_options_domain_name_servers: + default: + - AmazonProvidedDNS + description: Specify a list of DNS server addresses for DHCP options set, default + to AWS provided (requires enable_dhcp_options set to true) + items: + type: string + type: array + dhcp_options_netbios_name_servers: + default: [] + description: Specify a list of netbios servers for DHCP options set (requires + enable_dhcp_options set to true) + items: + type: string + type: array + dhcp_options_netbios_node_type: + default: "" + description: Specify netbios node_type for DHCP options set (requires enable_dhcp_options + set to true) + type: string + dhcp_options_ntp_servers: + default: [] + description: Specify a list of NTP servers for DHCP options set (requires enable_dhcp_options + set to true) + items: + type: string + type: array + dhcp_options_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the DHCP option set (requires enable_dhcp_options + set to true) + type: object + elasticache_acl_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the elasticache subnets network ACL + type: object + elasticache_dedicated_network_acl: + default: false + description: Whether to use dedicated network ACL (not default) and custom rules + for elasticache subnets + type: boolean + elasticache_inbound_acl_rules: + default: + - cidr_block: 0.0.0.0/0 + from_port: "0" + protocol: "-1" + rule_action: allow + rule_number: "100" + to_port: "0" + description: Elasticache subnets inbound network ACL rules + items: + additionalProperties: + type: string + type: object + type: array + elasticache_outbound_acl_rules: + default: + - cidr_block: 0.0.0.0/0 + from_port: "0" + protocol: "-1" + rule_action: allow + rule_number: "100" + to_port: "0" + description: Elasticache subnets outbound network ACL rules + items: + additionalProperties: + type: string + type: object + type: array + elasticache_route_table_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the elasticache route tables + type: object + elasticache_subnet_assign_ipv6_address_on_creation: + default: false + description: Specify true to indicate that network interfaces created in the specified + subnet should be assigned an IPv6 address. Default is `false` + type: boolean + elasticache_subnet_enable_dns64: + default: true + description: 'Indicates whether DNS queries made to the Amazon-provided DNS Resolver + in this subnet should return synthetic IPv6 addresses for IPv4-only destinations. + Default: `true`' + type: boolean + elasticache_subnet_enable_resource_name_dns_a_record_on_launch: + default: false + description: 'Indicates whether to respond to DNS queries for instance hostnames + with DNS A records. Default: `false`' + type: boolean + elasticache_subnet_enable_resource_name_dns_aaaa_record_on_launch: + default: true + description: 'Indicates whether to respond to DNS queries for instance hostnames + with DNS AAAA records. Default: `true`' + type: boolean + elasticache_subnet_group_name: + description: Name of elasticache subnet group + type: string + elasticache_subnet_group_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the elasticache subnet group + type: object + elasticache_subnet_ipv6_native: + default: false + description: 'Indicates whether to create an IPv6-only subnet. Default: `false`' + type: boolean + elasticache_subnet_ipv6_prefixes: + default: [] + description: Assigns IPv6 elasticache subnet id based on the Amazon provided /56 + prefix base 10 integer (0-256). Must be of equal length to the corresponding + IPv4 subnet list + items: + type: string + type: array + elasticache_subnet_names: + default: [] + description: Explicit values to use in the Name tag on elasticache subnets. If + empty, Name tags are generated + items: + type: string + type: array + elasticache_subnet_private_dns_hostname_type_on_launch: + description: 'The type of hostnames to assign to instances in the subnet at launch. + For IPv6-only subnets, an instance DNS name must be based on the instance ID. + For dual-stack and IPv4-only subnets, you can specify whether DNS names use + the instance IPv4 address or the instance ID. Valid values: `ip-name`, `resource-name`' + type: string + elasticache_subnet_suffix: + default: elasticache + description: Suffix to append to elasticache subnets name + type: string + elasticache_subnet_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the elasticache subnets + type: object + elasticache_subnets: + default: [] + description: A list of elasticache subnets inside the VPC + items: + type: string + type: array + enable_dhcp_options: + default: false + description: Should be true if you want to specify a DHCP options set with a custom + domain name, DNS servers, NTP servers, netbios servers, and/or netbios server + type + type: boolean + enable_dns_hostnames: + default: true + description: Should be true to enable DNS hostnames in the VPC + type: boolean + enable_dns_support: + default: true + description: Should be true to enable DNS support in the VPC + type: boolean + enable_flow_log: + default: false + description: Whether or not to enable VPC Flow Logs + type: boolean + enable_ipv6: + default: false + description: Requests an Amazon-provided IPv6 CIDR block with a /56 prefix length + for the VPC. You cannot specify the range of IP addresses, or the size of the + CIDR block + type: boolean + enable_nat_gateway: + default: false + description: Should be true if you want to provision NAT Gateways for each of + your private networks + type: boolean + enable_network_address_usage_metrics: + description: Determines whether network address usage metrics are enabled for + the VPC + type: boolean + enable_public_redshift: + default: false + description: Controls if redshift should have public routing table + type: boolean + enable_vpn_gateway: + default: false + description: Should be true if you want to create a new VPN Gateway resource and + attach it to the VPC + type: boolean + external_nat_ip_ids: + default: [] + description: List of EIP IDs to be assigned to the NAT Gateways (used in combination + with reuse_nat_ips) + items: + type: string + type: array + external_nat_ips: + default: [] + description: List of EIPs to be used for `nat_public_ips` output (used in combination + with reuse_nat_ips and external_nat_ip_ids) + items: + type: string + type: array + flow_log_cloudwatch_iam_role_arn: + default: "" + description: The ARN for the IAM role that's used to post flow logs to a CloudWatch + Logs log group. When flow_log_destination_arn is set to ARN of Cloudwatch Logs, + this argument needs to be provided + type: string + flow_log_cloudwatch_log_group_class: + description: 'Specified the log class of the log group. Possible values are: STANDARD + or INFREQUENT_ACCESS' + type: string + flow_log_cloudwatch_log_group_kms_key_id: + description: The ARN of the KMS Key to use when encrypting log data for VPC flow + logs + type: string + flow_log_cloudwatch_log_group_name_prefix: + default: /aws/vpc-flow-log/ + description: Specifies the name prefix of CloudWatch Log Group for VPC flow logs + type: string + flow_log_cloudwatch_log_group_name_suffix: + default: "" + description: Specifies the name suffix of CloudWatch Log Group for VPC flow logs + type: string + flow_log_cloudwatch_log_group_retention_in_days: + description: Specifies the number of days you want to retain log events in the + specified log group for VPC flow logs + type: number + flow_log_cloudwatch_log_group_skip_destroy: + default: false + description: ' Set to true if you do not wish the log group (and any logs it may + contain) to be deleted at destroy time, and instead just remove the log group + from the Terraform state' + type: boolean + flow_log_deliver_cross_account_role: + description: (Optional) ARN of the IAM role that allows Amazon EC2 to publish + flow logs across accounts. + type: string + flow_log_destination_arn: + default: "" + description: The ARN of the CloudWatch log group or S3 bucket where VPC Flow Logs + will be pushed. If this ARN is a S3 bucket the appropriate permissions need + to be set on that bucket's policy. When create_flow_log_cloudwatch_log_group + is set to false this argument must be provided + type: string + flow_log_destination_type: + default: cloud-watch-logs + description: Type of flow log destination. Can be s3, kinesis-data-firehose or + cloud-watch-logs + type: string + flow_log_file_format: + description: '(Optional) The format for the flow log. Valid values: `plain-text`, + `parquet`' + type: string + flow_log_hive_compatible_partitions: + default: false + description: (Optional) Indicates whether to use Hive-compatible prefixes for + flow logs stored in Amazon S3 + type: boolean + flow_log_log_format: + description: The fields to include in the flow log record, in the order in which + they should appear + type: string + flow_log_max_aggregation_interval: + default: 600 + description: 'The maximum interval of time during which a flow of packets is captured + and aggregated into a flow log record. Valid Values: `60` seconds or `600` seconds' + type: number + flow_log_per_hour_partition: + default: false + description: (Optional) Indicates whether to partition the flow log per hour. + This reduces the cost and response time for queries + type: boolean + flow_log_traffic_type: + default: ALL + description: 'The type of traffic to capture. Valid values: ACCEPT, REJECT, ALL' + type: string + igw_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the internet gateway + type: object + instance_tenancy: + default: default + description: A tenancy option for instances launched into the VPC + type: string + intra_acl_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the intra subnets network ACL + type: object + intra_dedicated_network_acl: + default: false + description: Whether to use dedicated network ACL (not default) and custom rules + for intra subnets + type: boolean + intra_inbound_acl_rules: + default: + - cidr_block: 0.0.0.0/0 + from_port: "0" + protocol: "-1" + rule_action: allow + rule_number: "100" + to_port: "0" + description: Intra subnets inbound network ACLs + items: + additionalProperties: + type: string + type: object + type: array + intra_outbound_acl_rules: + default: + - cidr_block: 0.0.0.0/0 + from_port: "0" + protocol: "-1" + rule_action: allow + rule_number: "100" + to_port: "0" + description: Intra subnets outbound network ACLs + items: + additionalProperties: + type: string + type: object + type: array + intra_route_table_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the intra route tables + type: object + intra_subnet_assign_ipv6_address_on_creation: + default: false + description: Specify true to indicate that network interfaces created in the specified + subnet should be assigned an IPv6 address. Default is `false` + type: boolean + intra_subnet_enable_dns64: + default: true + description: 'Indicates whether DNS queries made to the Amazon-provided DNS Resolver + in this subnet should return synthetic IPv6 addresses for IPv4-only destinations. + Default: `true`' + type: boolean + intra_subnet_enable_resource_name_dns_a_record_on_launch: + default: false + description: 'Indicates whether to respond to DNS queries for instance hostnames + with DNS A records. Default: `false`' + type: boolean + intra_subnet_enable_resource_name_dns_aaaa_record_on_launch: + default: true + description: 'Indicates whether to respond to DNS queries for instance hostnames + with DNS AAAA records. Default: `true`' + type: boolean + intra_subnet_ipv6_native: + default: false + description: 'Indicates whether to create an IPv6-only subnet. Default: `false`' + type: boolean + intra_subnet_ipv6_prefixes: + default: [] + description: Assigns IPv6 intra subnet id based on the Amazon provided /56 prefix + base 10 integer (0-256). Must be of equal length to the corresponding IPv4 subnet + list + items: + type: string + type: array + intra_subnet_names: + default: [] + description: Explicit values to use in the Name tag on intra subnets. If empty, + Name tags are generated + items: + type: string + type: array + intra_subnet_private_dns_hostname_type_on_launch: + description: 'The type of hostnames to assign to instances in the subnet at launch. + For IPv6-only subnets, an instance DNS name must be based on the instance ID. + For dual-stack and IPv4-only subnets, you can specify whether DNS names use + the instance IPv4 address or the instance ID. Valid values: `ip-name`, `resource-name`' + type: string + intra_subnet_suffix: + default: intra + description: Suffix to append to intra subnets name + type: string + intra_subnet_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the intra subnets + type: object + intra_subnets: + default: [] + description: A list of intra subnets inside the VPC + items: + type: string + type: array + ipv4_ipam_pool_id: + description: (Optional) The ID of an IPv4 IPAM pool you want to use for allocating + this VPC's CIDR + type: string + ipv4_netmask_length: + description: (Optional) The netmask length of the IPv4 CIDR you want to allocate + to this VPC. Requires specifying a ipv4_ipam_pool_id + type: number + ipv6_cidr: + description: (Optional) IPv6 CIDR block to request from an IPAM Pool. Can be set + explicitly or derived from IPAM using `ipv6_netmask_length` + type: string + ipv6_cidr_block_network_border_group: + description: By default when an IPv6 CIDR is assigned to a VPC a default ipv6_cidr_block_network_border_group + will be set to the region of the VPC. This can be changed to restrict advertisement + of public addresses to specific Network Border Groups such as LocalZones + type: string + ipv6_ipam_pool_id: + description: (Optional) IPAM Pool ID for a IPv6 pool. Conflicts with `assign_generated_ipv6_cidr_block` + type: string + ipv6_netmask_length: + description: '(Optional) Netmask length to request from IPAM Pool. Conflicts with + `ipv6_cidr_block`. This can be omitted if IPAM pool as a `allocation_default_netmask_length` + set. Valid values: `56`' + type: number + manage_default_network_acl: + default: true + description: Should be true to adopt and manage Default Network ACL + type: boolean + manage_default_route_table: + default: true + description: Should be true to manage default route table + type: boolean + manage_default_security_group: + default: true + description: Should be true to adopt and manage default security group + type: boolean + manage_default_vpc: + default: false + description: Should be true to adopt and manage Default VPC + type: boolean + map_customer_owned_ip_on_launch: + default: false + description: Specify true to indicate that network interfaces created in the subnet + should be assigned a customer owned IP address. The `customer_owned_ipv4_pool` + and `outpost_arn` arguments must be specified when set to `true`. Default is + `false` + type: boolean + map_public_ip_on_launch: + default: false + description: Specify true to indicate that instances launched into the subnet + should be assigned a public IP address. Default is `false` + type: boolean + name: + default: "" + description: Name to be used on all the resources as identifier + type: string + nat_eip_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the NAT EIP + type: object + nat_gateway_destination_cidr_block: + default: 0.0.0.0/0 + description: Used to pass a custom destination route for private NAT Gateway. + If not specified, the default 0.0.0.0/0 is used as a destination route + type: string + nat_gateway_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the NAT gateways + type: object + one_nat_gateway_per_az: + default: false + description: Should be true if you want only one NAT Gateway per availability + zone. Requires `var.azs` to be set, and the number of `public_subnets` created + to be greater than or equal to the number of availability zones specified in + `var.azs` + type: boolean + outpost_acl_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the outpost subnets network ACL + type: object + outpost_arn: + description: ARN of Outpost you want to create a subnet in + type: string + outpost_az: + description: AZ where Outpost is anchored + type: string + outpost_dedicated_network_acl: + default: false + description: Whether to use dedicated network ACL (not default) and custom rules + for outpost subnets + type: boolean + outpost_inbound_acl_rules: + default: + - cidr_block: 0.0.0.0/0 + from_port: "0" + protocol: "-1" + rule_action: allow + rule_number: "100" + to_port: "0" + description: Outpost subnets inbound network ACLs + items: + additionalProperties: + type: string + type: object + type: array + outpost_outbound_acl_rules: + default: + - cidr_block: 0.0.0.0/0 + from_port: "0" + protocol: "-1" + rule_action: allow + rule_number: "100" + to_port: "0" + description: Outpost subnets outbound network ACLs + items: + additionalProperties: + type: string + type: object + type: array + outpost_subnet_assign_ipv6_address_on_creation: + default: false + description: Specify true to indicate that network interfaces created in the specified + subnet should be assigned an IPv6 address. Default is `false` + type: boolean + outpost_subnet_enable_dns64: + default: true + description: 'Indicates whether DNS queries made to the Amazon-provided DNS Resolver + in this subnet should return synthetic IPv6 addresses for IPv4-only destinations. + Default: `true`' + type: boolean + outpost_subnet_enable_resource_name_dns_a_record_on_launch: + default: false + description: 'Indicates whether to respond to DNS queries for instance hostnames + with DNS A records. Default: `false`' + type: boolean + outpost_subnet_enable_resource_name_dns_aaaa_record_on_launch: + default: true + description: 'Indicates whether to respond to DNS queries for instance hostnames + with DNS AAAA records. Default: `true`' + type: boolean + outpost_subnet_ipv6_native: + default: false + description: 'Indicates whether to create an IPv6-only subnet. Default: `false`' + type: boolean + outpost_subnet_ipv6_prefixes: + default: [] + description: Assigns IPv6 outpost subnet id based on the Amazon provided /56 prefix + base 10 integer (0-256). Must be of equal length to the corresponding IPv4 subnet + list + items: + type: string + type: array + outpost_subnet_names: + default: [] + description: Explicit values to use in the Name tag on outpost subnets. If empty, + Name tags are generated + items: + type: string + type: array + outpost_subnet_private_dns_hostname_type_on_launch: + description: 'The type of hostnames to assign to instances in the subnet at launch. + For IPv6-only subnets, an instance DNS name must be based on the instance ID. + For dual-stack and IPv4-only subnets, you can specify whether DNS names use + the instance IPv4 address or the instance ID. Valid values: `ip-name`, `resource-name`' + type: string + outpost_subnet_suffix: + default: outpost + description: Suffix to append to outpost subnets name + type: string + outpost_subnet_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the outpost subnets + type: object + outpost_subnets: + default: [] + description: A list of outpost subnets inside the VPC + items: + type: string + type: array + private_acl_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the private subnets network ACL + type: object + private_dedicated_network_acl: + default: false + description: Whether to use dedicated network ACL (not default) and custom rules + for private subnets + type: boolean + private_inbound_acl_rules: + default: + - cidr_block: 0.0.0.0/0 + from_port: "0" + protocol: "-1" + rule_action: allow + rule_number: "100" + to_port: "0" + description: Private subnets inbound network ACLs + items: + additionalProperties: + type: string + type: object + type: array + private_outbound_acl_rules: + default: + - cidr_block: 0.0.0.0/0 + from_port: "0" + protocol: "-1" + rule_action: allow + rule_number: "100" + to_port: "0" + description: Private subnets outbound network ACLs + items: + additionalProperties: + type: string + type: object + type: array + private_route_table_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the private route tables + type: object + private_subnet_assign_ipv6_address_on_creation: + default: false + description: Specify true to indicate that network interfaces created in the specified + subnet should be assigned an IPv6 address. Default is `false` + type: boolean + private_subnet_enable_dns64: + default: true + description: 'Indicates whether DNS queries made to the Amazon-provided DNS Resolver + in this subnet should return synthetic IPv6 addresses for IPv4-only destinations. + Default: `true`' + type: boolean + private_subnet_enable_resource_name_dns_a_record_on_launch: + default: false + description: 'Indicates whether to respond to DNS queries for instance hostnames + with DNS A records. Default: `false`' + type: boolean + private_subnet_enable_resource_name_dns_aaaa_record_on_launch: + default: true + description: 'Indicates whether to respond to DNS queries for instance hostnames + with DNS AAAA records. Default: `true`' + type: boolean + private_subnet_ipv6_native: + default: false + description: 'Indicates whether to create an IPv6-only subnet. Default: `false`' + type: boolean + private_subnet_ipv6_prefixes: + default: [] + description: Assigns IPv6 private subnet id based on the Amazon provided /56 prefix + base 10 integer (0-256). Must be of equal length to the corresponding IPv4 subnet + list + items: + type: string + type: array + private_subnet_names: + default: [] + description: Explicit values to use in the Name tag on private subnets. If empty, + Name tags are generated + items: + type: string + type: array + private_subnet_private_dns_hostname_type_on_launch: + description: 'The type of hostnames to assign to instances in the subnet at launch. + For IPv6-only subnets, an instance DNS name must be based on the instance ID. + For dual-stack and IPv4-only subnets, you can specify whether DNS names use + the instance IPv4 address or the instance ID. Valid values: `ip-name`, `resource-name`' + type: string + private_subnet_suffix: + default: private + description: Suffix to append to private subnets name + type: string + private_subnet_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the private subnets + type: object + private_subnet_tags_per_az: + additionalProperties: + additionalProperties: + type: string + type: object + default: {} + description: Additional tags for the private subnets where the primary key is + the AZ + type: object + private_subnets: + default: [] + description: A list of private subnets inside the VPC + items: + type: string + type: array + propagate_intra_route_tables_vgw: + default: false + description: Should be true if you want route table propagation + type: boolean + propagate_private_route_tables_vgw: + default: false + description: Should be true if you want route table propagation + type: boolean + propagate_public_route_tables_vgw: + default: false + description: Should be true if you want route table propagation + type: boolean + public_acl_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the public subnets network ACL + type: object + public_dedicated_network_acl: + default: false + description: Whether to use dedicated network ACL (not default) and custom rules + for public subnets + type: boolean + public_inbound_acl_rules: + default: + - cidr_block: 0.0.0.0/0 + from_port: "0" + protocol: "-1" + rule_action: allow + rule_number: "100" + to_port: "0" + description: Public subnets inbound network ACLs + items: + additionalProperties: + type: string + type: object + type: array + public_outbound_acl_rules: + default: + - cidr_block: 0.0.0.0/0 + from_port: "0" + protocol: "-1" + rule_action: allow + rule_number: "100" + to_port: "0" + description: Public subnets outbound network ACLs + items: + additionalProperties: + type: string + type: object + type: array + public_route_table_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the public route tables + type: object + public_subnet_assign_ipv6_address_on_creation: + default: false + description: Specify true to indicate that network interfaces created in the specified + subnet should be assigned an IPv6 address. Default is `false` + type: boolean + public_subnet_enable_dns64: + default: true + description: 'Indicates whether DNS queries made to the Amazon-provided DNS Resolver + in this subnet should return synthetic IPv6 addresses for IPv4-only destinations. + Default: `true`' + type: boolean + public_subnet_enable_resource_name_dns_a_record_on_launch: + default: false + description: 'Indicates whether to respond to DNS queries for instance hostnames + with DNS A records. Default: `false`' + type: boolean + public_subnet_enable_resource_name_dns_aaaa_record_on_launch: + default: true + description: 'Indicates whether to respond to DNS queries for instance hostnames + with DNS AAAA records. Default: `true`' + type: boolean + public_subnet_ipv6_native: + default: false + description: 'Indicates whether to create an IPv6-only subnet. Default: `false`' + type: boolean + public_subnet_ipv6_prefixes: + default: [] + description: Assigns IPv6 public subnet id based on the Amazon provided /56 prefix + base 10 integer (0-256). Must be of equal length to the corresponding IPv4 subnet + list + items: + type: string + type: array + public_subnet_names: + default: [] + description: Explicit values to use in the Name tag on public subnets. If empty, + Name tags are generated + items: + type: string + type: array + public_subnet_private_dns_hostname_type_on_launch: + description: 'The type of hostnames to assign to instances in the subnet at launch. + For IPv6-only subnets, an instance DNS name must be based on the instance ID. + For dual-stack and IPv4-only subnets, you can specify whether DNS names use + the instance IPv4 address or the instance ID. Valid values: `ip-name`, `resource-name`' + type: string + public_subnet_suffix: + default: public + description: Suffix to append to public subnets name + type: string + public_subnet_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the public subnets + type: object + public_subnet_tags_per_az: + additionalProperties: + additionalProperties: + type: string + type: object + default: {} + description: Additional tags for the public subnets where the primary key is the + AZ + type: object + public_subnets: + default: [] + description: A list of public subnets inside the VPC + items: + type: string + type: array + putin_khuylo: + default: true + description: 'Do you agree that Putin doesn''t respect Ukrainian sovereignty and + territorial integrity? More info: https://en.wikipedia.org/wiki/Putin_khuylo!' + type: boolean + redshift_acl_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the redshift subnets network ACL + type: object + redshift_dedicated_network_acl: + default: false + description: Whether to use dedicated network ACL (not default) and custom rules + for redshift subnets + type: boolean + redshift_inbound_acl_rules: + default: + - cidr_block: 0.0.0.0/0 + from_port: "0" + protocol: "-1" + rule_action: allow + rule_number: "100" + to_port: "0" + description: Redshift subnets inbound network ACL rules + items: + additionalProperties: + type: string + type: object + type: array + redshift_outbound_acl_rules: + default: + - cidr_block: 0.0.0.0/0 + from_port: "0" + protocol: "-1" + rule_action: allow + rule_number: "100" + to_port: "0" + description: Redshift subnets outbound network ACL rules + items: + additionalProperties: + type: string + type: object + type: array + redshift_route_table_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the redshift route tables + type: object + redshift_subnet_assign_ipv6_address_on_creation: + default: false + description: Specify true to indicate that network interfaces created in the specified + subnet should be assigned an IPv6 address. Default is `false` + type: boolean + redshift_subnet_enable_dns64: + default: true + description: 'Indicates whether DNS queries made to the Amazon-provided DNS Resolver + in this subnet should return synthetic IPv6 addresses for IPv4-only destinations. + Default: `true`' + type: boolean + redshift_subnet_enable_resource_name_dns_a_record_on_launch: + default: false + description: 'Indicates whether to respond to DNS queries for instance hostnames + with DNS A records. Default: `false`' + type: boolean + redshift_subnet_enable_resource_name_dns_aaaa_record_on_launch: + default: true + description: 'Indicates whether to respond to DNS queries for instance hostnames + with DNS AAAA records. Default: `true`' + type: boolean + redshift_subnet_group_name: + description: Name of redshift subnet group + type: string + redshift_subnet_group_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the redshift subnet group + type: object + redshift_subnet_ipv6_native: + default: false + description: 'Indicates whether to create an IPv6-only subnet. Default: `false`' + type: boolean + redshift_subnet_ipv6_prefixes: + default: [] + description: Assigns IPv6 redshift subnet id based on the Amazon provided /56 + prefix base 10 integer (0-256). Must be of equal length to the corresponding + IPv4 subnet list + items: + type: string + type: array + redshift_subnet_names: + default: [] + description: Explicit values to use in the Name tag on redshift subnets. If empty, + Name tags are generated + items: + type: string + type: array + redshift_subnet_private_dns_hostname_type_on_launch: + description: 'The type of hostnames to assign to instances in the subnet at launch. + For IPv6-only subnets, an instance DNS name must be based on the instance ID. + For dual-stack and IPv4-only subnets, you can specify whether DNS names use + the instance IPv4 address or the instance ID. Valid values: `ip-name`, `resource-name`' + type: string + redshift_subnet_suffix: + default: redshift + description: Suffix to append to redshift subnets name + type: string + redshift_subnet_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the redshift subnets + type: object + redshift_subnets: + default: [] + description: A list of redshift subnets inside the VPC + items: + type: string + type: array + reuse_nat_ips: + default: false + description: Should be true if you don't want EIPs to be created for your NAT + Gateways and will instead pass them in via the 'external_nat_ip_ids' variable + type: boolean + secondary_cidr_blocks: + default: [] + description: List of secondary CIDR blocks to associate with the VPC to extend + the IP Address pool + items: + type: string + type: array + single_nat_gateway: + default: false + description: Should be true if you want to provision a single shared NAT Gateway + across all of your private networks + type: boolean + tags: + additionalProperties: + type: string + default: {} + description: A map of tags to add to all resources + type: object + use_ipam_pool: + default: false + description: Determines whether IPAM pool is used for CIDR allocation + type: boolean + vpc_flow_log_permissions_boundary: + description: The ARN of the Permissions Boundary for the VPC Flow Log IAM Role + type: string + vpc_flow_log_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the VPC Flow Logs + type: object + vpc_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the VPC + type: object + vpn_gateway_az: + description: The Availability Zone for the VPN Gateway + type: string + vpn_gateway_id: + default: "" + description: ID of VPN Gateway to attach to the VPC + type: string + vpn_gateway_tags: + additionalProperties: + type: string + default: {} + description: Additional tags for the VPN gateway + type: object + type: object + + served: true + storage: true diff --git a/test/assets/terraform/api/local.yaml b/test/assets/terraform/api/local.yaml new file mode 100644 index 0000000..8a50616 --- /dev/null +++ b/test/assets/terraform/api/local.yaml @@ -0,0 +1,35 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: examples.example.com +spec: + group: example.com + names: + kind: Example + plural: examples + singular: example + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + type: object + properties: + spec: + default: {} + properties: + name: + description: Name for the resource + type: string + size: + default: 1 + type: number + tags: + additionalProperties: + type: string + default: {} + type: object + type: object + + served: true + storage: true diff --git a/test/assets/terraform/api/nested-registry.yaml b/test/assets/terraform/api/nested-registry.yaml new file mode 100644 index 0000000..6ea5a17 --- /dev/null +++ b/test/assets/terraform/api/nested-registry.yaml @@ -0,0 +1,75 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: examples.example.com +spec: + group: example.com + names: + kind: Example + plural: examples + singular: example + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + type: object + properties: + spec: + default: {} + properties: + create: + default: true + description: Determines whether resources will be created + type: boolean + create_security_group: + default: false + description: Determines if a security group is created + type: boolean + security_group_description: + description: Description of the security group created + type: string + security_group_ids: + default: [] + description: Default security group IDs to associate with the VPC endpoints + items: + type: string + type: array + security_group_name: + description: Name to use on security group created. Conflicts with `security_group_name_prefix` + type: string + security_group_name_prefix: + description: Name prefix to use on security group created. Conflicts with `security_group_name` + type: string + security_group_tags: + additionalProperties: + type: string + default: {} + description: A map of additional tags to add to the security group created + type: object + subnet_ids: + default: [] + description: Default subnets IDs to associate with the VPC endpoints + items: + type: string + type: array + tags: + additionalProperties: + type: string + default: {} + description: A map of tags to use on all resources + type: object + timeouts: + additionalProperties: + type: string + default: {} + description: Define maximum timeout for creating, updating, and deleting VPC endpoint + resources + type: object + vpc_id: + description: The ID of the VPC in which the endpoint will be used + type: string + type: object + + served: true + storage: true diff --git a/test/assets/terraform/api/registry.yaml b/test/assets/terraform/api/registry.yaml new file mode 100644 index 0000000..2034768 --- /dev/null +++ b/test/assets/terraform/api/registry.yaml @@ -0,0 +1,218 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: examples.example.com +spec: + group: example.com + names: + kind: Example + plural: examples + singular: example + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + type: object + properties: + spec: + default: {} + properties: + acceleration_status: + description: (Optional) Sets the accelerate configuration of an existing bucket. + Can be Enabled or Suspended. + type: string + access_log_delivery_policy_source_accounts: + default: [] + description: (Optional) List of AWS Account IDs should be allowed to deliver access + logs to this bucket. + items: + type: string + type: array + access_log_delivery_policy_source_buckets: + default: [] + description: (Optional) List of S3 bucket ARNs which should be allowed to deliver + access logs to this bucket. + items: + type: string + type: array + acl: + description: (Optional) The canned ACL to apply. Conflicts with `grant` + type: string + allowed_kms_key_arn: + description: The ARN of KMS key which should be allowed in PutObject + type: string + analytics_self_source_destination: + default: false + description: Whether or not the analytics source bucket is also the destination + bucket. + type: boolean + analytics_source_account_id: + description: The analytics source account id. + type: string + analytics_source_bucket_arn: + description: The analytics source bucket ARN. + type: string + attach_access_log_delivery_policy: + default: false + description: Controls if S3 bucket should have S3 access log delivery policy attached + type: boolean + attach_analytics_destination_policy: + default: false + description: Controls if S3 bucket should have bucket analytics destination policy + attached. + type: boolean + attach_deny_incorrect_encryption_headers: + default: false + description: Controls if S3 bucket should deny incorrect encryption headers policy + attached. + type: boolean + attach_deny_incorrect_kms_key_sse: + default: false + description: Controls if S3 bucket policy should deny usage of incorrect KMS key + SSE. + type: boolean + attach_deny_insecure_transport_policy: + default: false + description: Controls if S3 bucket should have deny non-SSL transport policy attached + type: boolean + attach_deny_unencrypted_object_uploads: + default: false + description: Controls if S3 bucket should deny unencrypted object uploads policy + attached. + type: boolean + attach_elb_log_delivery_policy: + default: false + description: Controls if S3 bucket should have ELB log delivery policy attached + type: boolean + attach_inventory_destination_policy: + default: false + description: Controls if S3 bucket should have bucket inventory destination policy + attached. + type: boolean + attach_lb_log_delivery_policy: + default: false + description: Controls if S3 bucket should have ALB/NLB log delivery policy attached + type: boolean + attach_policy: + default: false + description: Controls if S3 bucket should have bucket policy attached (set to + `true` to use value of `policy` as bucket policy) + type: boolean + attach_public_policy: + default: true + description: Controls if a user defined public bucket policy will be attached + (set to `false` to allow upstream to apply defaults to the bucket) + type: boolean + attach_require_latest_tls_policy: + default: false + description: Controls if S3 bucket should require the latest version of TLS + type: boolean + block_public_acls: + default: true + description: Whether Amazon S3 should block public ACLs for this bucket. + type: boolean + block_public_policy: + default: true + description: Whether Amazon S3 should block public bucket policies for this bucket. + type: boolean + bucket: + description: (Optional, Forces new resource) The name of the bucket. If omitted, + Terraform will assign a random, unique name. + type: string + bucket_prefix: + description: (Optional, Forces new resource) Creates a unique bucket name beginning + with the specified prefix. Conflicts with bucket. + type: string + control_object_ownership: + default: false + description: Whether to manage S3 Bucket Ownership Controls on this bucket. + type: boolean + create_bucket: + default: true + description: Controls if S3 bucket should be created + type: boolean + expected_bucket_owner: + description: The account ID of the expected bucket owner + type: string + force_destroy: + default: false + description: (Optional, Default:false ) A boolean that indicates all objects should + be deleted from the bucket so that the bucket can be destroyed without error. + These objects are not recoverable. + type: boolean + ignore_public_acls: + default: true + description: Whether Amazon S3 should ignore public ACLs for this bucket. + type: boolean + inventory_self_source_destination: + default: false + description: Whether or not the inventory source bucket is also the destination + bucket. + type: boolean + inventory_source_account_id: + description: The inventory source account id. + type: string + inventory_source_bucket_arn: + description: The inventory source bucket ARN. + type: string + object_lock_enabled: + default: false + description: Whether S3 bucket should have an Object Lock configuration enabled. + type: boolean + object_ownership: + default: BucketOwnerEnforced + description: 'Object ownership. Valid values: BucketOwnerEnforced, BucketOwnerPreferred + or ObjectWriter. ''BucketOwnerEnforced'': ACLs are disabled, and the bucket + owner automatically owns and has full control over every object in the bucket. + ''BucketOwnerPreferred'': Objects uploaded to the bucket change ownership to + the bucket owner if the objects are uploaded with the bucket-owner-full-control + canned ACL. ''ObjectWriter'': The uploading account will own the object if the + object is uploaded with the bucket-owner-full-control canned ACL.' + type: string + owner: + additionalProperties: + type: string + default: {} + description: Bucket owner's display name and ID. Conflicts with `acl` + type: object + policy: + description: (Optional) A valid bucket policy JSON document. Note that if the + policy document is not specific enough (but still valid), Terraform may view + the policy as constantly changing in a terraform plan. In this case, please + make sure you use the verbose/specific version of the policy. For more information + about building AWS IAM policy documents with Terraform, see the AWS IAM Policy + Document Guide. + type: string + putin_khuylo: + default: true + description: 'Do you agree that Putin doesn''t respect Ukrainian sovereignty and + territorial integrity? More info: https://en.wikipedia.org/wiki/Putin_khuylo!' + type: boolean + request_payer: + description: (Optional) Specifies who should bear the cost of Amazon S3 data transfer. + Can be either BucketOwner or Requester. By default, the owner of the S3 bucket + would incur the costs of any data transfer. See Requester Pays Buckets developer + guide for more information. + type: string + restrict_public_buckets: + default: true + description: Whether Amazon S3 should restrict public bucket policies for this + bucket. + type: boolean + tags: + additionalProperties: + type: string + default: {} + description: (Optional) A mapping of tags to assign to the bucket. + type: object + versioning: + additionalProperties: + type: string + default: {} + description: Map containing versioning configuration. + type: object + type: object + + served: true + storage: true diff --git a/test/assets/terraform/expected-output-vpc/promise.yaml b/test/assets/terraform/expected-output-vpc/promise.yaml index 511f88b..6c8be3d 100644 --- a/test/assets/terraform/expected-output-vpc/promise.yaml +++ b/test/assets/terraform/expected-output-vpc/promise.yaml @@ -79,6 +79,6 @@ spec: - env: - name: MODULE_SOURCE value: git::https://github.com/GoogleCloudPlatform/cloud-foundation-fabric.git//modules/api-gateway?ref=v49.1.0 - image: ghcr.io/syntasso/kratix-cli/terraform-generate:v0.2.0 + image: ghcr.io/syntasso/kratix-cli/terraform-generate:v0.4.0 name: terraform-generate diff --git a/test/assets/terraform/expected-output-with-split/workflows/resource/configure/workflow.yaml b/test/assets/terraform/expected-output-with-split/workflows/resource/configure/workflow.yaml index 59cfb82..d6567f7 100644 --- a/test/assets/terraform/expected-output-with-split/workflows/resource/configure/workflow.yaml +++ b/test/assets/terraform/expected-output-with-split/workflows/resource/configure/workflow.yaml @@ -7,5 +7,5 @@ - env: - name: MODULE_SOURCE value: git::https://github.com/GoogleCloudPlatform/terraform-google-cloud-run?ref=v0.16.4 - image: ghcr.io/syntasso/kratix-cli/terraform-generate:v0.2.0 + image: ghcr.io/syntasso/kratix-cli/terraform-generate:v0.4.0 name: terraform-generate diff --git a/test/assets/terraform/expected-output/promise.yaml b/test/assets/terraform/expected-output/promise.yaml index 1f10470..e52315a 100644 --- a/test/assets/terraform/expected-output/promise.yaml +++ b/test/assets/terraform/expected-output/promise.yaml @@ -214,6 +214,6 @@ spec: - env: - name: MODULE_SOURCE value: git::https://github.com/GoogleCloudPlatform/terraform-google-cloud-run?ref=v0.16.4 - image: ghcr.io/syntasso/kratix-cli/terraform-generate:v0.2.0 + image: ghcr.io/syntasso/kratix-cli/terraform-generate:v0.4.0 name: terraform-generate diff --git a/test/assets/terraform/modules/local/basic/variables.tf b/test/assets/terraform/modules/local/basic/variables.tf new file mode 100644 index 0000000..68b6398 --- /dev/null +++ b/test/assets/terraform/modules/local/basic/variables.tf @@ -0,0 +1,14 @@ +variable "name" { + type = string + description = "Name for the resource" +} + +variable "size" { + type = number + default = 1 +} + +variable "tags" { + type = map(string) + default = {} +} diff --git a/test/init_tf_module_sources_test.go b/test/init_tf_module_sources_test.go new file mode 100644 index 0000000..fb6df34 --- /dev/null +++ b/test/init_tf_module_sources_test.go @@ -0,0 +1,184 @@ +package integration_test + +import ( + "os" + "path/filepath" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/onsi/gomega/gbytes" + "github.com/onsi/gomega/gexec" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "sigs.k8s.io/yaml" +) + +type moduleTestCase struct { + name string + moduleSource string + moduleRegistryVer string + expectRegistryEnv bool + expectedAPIPath string +} + +var _ = Describe("InitTerraformPromise source integration", func() { + cwd, err := os.Getwd() + Expect(err).NotTo(HaveOccurred()) + + localModulePath := filepath.Join(cwd, "assets", "terraform", "modules", "local", "basic") + vpcAPI := filepath.Join(cwd, "assets", "terraform", "api", "git.yaml") + vpcSubdirAPI := filepath.Join(cwd, "assets", "terraform", "api", "git-subdir.yaml") + s3API := filepath.Join(cwd, "assets", "terraform", "api", "registry.yaml") + nestedAPI := filepath.Join(cwd, "assets", "terraform", "api", "nested-registry.yaml") + localAPI := filepath.Join(cwd, "assets", "terraform", "api", "local.yaml") + + DescribeTable("generates promise schema and workflow envs", + func(tc moduleTestCase) { + workingDir, err := os.MkdirTemp("", "kratix-test-sources") + Expect(err).NotTo(HaveOccurred()) + defer os.RemoveAll(workingDir) + + r := &runner{ + exitCode: 0, + flags: map[string]string{ + "--group": "example.com", + "--kind": "Example", + "--version": "v1alpha1", + "--dir": workingDir, + "--split": "", + }, + } + + r.flags["--module-source"] = tc.moduleSource + if tc.moduleRegistryVer != "" { + r.flags["--module-registry-version"] = tc.moduleRegistryVer + } + + session := r.run("init", "tf-module-promise", "example") + Expect(session).To(gexec.Exit(0)) + Expect(session.Out).To(gbytes.Say("Promise generated successfully")) + + envs := readWorkflowEnvs(workingDir) + Expect(envs["MODULE_SOURCE"]).To(Equal(tc.moduleSource)) + if tc.expectRegistryEnv { + Expect(envs).To(HaveKeyWithValue("MODULE_REGISTRY_VERSION", tc.moduleRegistryVer)) + } else { + Expect(envs).NotTo(HaveKey("MODULE_REGISTRY_VERSION")) + } + + actual := readSpecTypes(workingDir) + expected := readCRDTypes(tc.expectedAPIPath) + Expect(actual).To(Equal(expected)) + + actualCRD := readCRD(workingDir) + expectedCRD := readCRDFromPath(tc.expectedAPIPath) + Expect(actualCRD).To(Equal(expectedCRD)) + }, + Entry("open source git repo with ref", + moduleTestCase{ + name: "git vpc", + moduleSource: "git::https://github.com/terraform-aws-modules/terraform-aws-vpc.git?ref=v5.7.0", + expectedAPIPath: vpcAPI, + }, + ), + Entry("private git repo placeholder with subdir (mono-repo style)", + moduleTestCase{ + name: "git subdir", + moduleSource: "git::ssh://git@github.com/syntasso/kratix-cli-private-tf-module-test-fixture.git//modules/vpc-endpoints?ref=v5.7.0", + expectedAPIPath: vpcSubdirAPI, + }, + ), + Entry("registry with version", + moduleTestCase{ + name: "registry s3 bucket", + moduleSource: "terraform-aws-modules/s3-bucket/aws", + moduleRegistryVer: "4.1.2", + expectRegistryEnv: true, + expectedAPIPath: s3API, + }, + ), + Entry("nested registry with version", + moduleTestCase{ + name: "nested registry vpc endpoints", + moduleSource: "terraform-aws-modules/vpc/aws//modules/vpc-endpoints", + moduleRegistryVer: "5.7.0", + expectRegistryEnv: true, + expectedAPIPath: nestedAPI, + }, + ), + Entry("local filesystem module", + moduleTestCase{ + name: "local module", + moduleSource: localModulePath, + expectedAPIPath: localAPI, + }, + ), + ) +}) + +func readWorkflowEnvs(workingDir string) map[string]string { + workflowsPath := filepath.Join(workingDir, "workflows", "resource", "configure", "workflow.yaml") + bytes, err := os.ReadFile(workflowsPath) + Expect(err).NotTo(HaveOccurred()) + + var pipelines []map[string]any + Expect(yaml.Unmarshal(bytes, &pipelines)).To(Succeed()) + Expect(pipelines).ToNot(BeEmpty()) + + spec, ok := pipelines[0]["spec"].(map[string]any) + Expect(ok).To(BeTrue()) + containers, ok := spec["containers"].([]any) + Expect(ok).To(BeTrue()) + Expect(containers).ToNot(BeEmpty()) + firstContainer, ok := containers[0].(map[string]any) + Expect(ok).To(BeTrue()) + envList, ok := firstContainer["env"].([]any) + Expect(ok).To(BeTrue()) + + envs := map[string]string{} + for _, item := range envList { + entry, ok := item.(map[string]any) + Expect(ok).To(BeTrue()) + name, _ := entry["name"].(string) + value, _ := entry["value"].(string) + envs[name] = value + } + return envs +} + +func readSpecTypes(workingDir string) map[string]string { + crd := readCRD(workingDir) + specProps := crd.Spec.Versions[0].Schema.OpenAPIV3Schema.Properties["spec"].Properties + + result := map[string]string{} + for name, prop := range specProps { + result[name] = prop.Type + } + return result +} + +func readCRDTypes(fixturePath string) map[string]string { + expected := readCRDFromPath(fixturePath) + specProps := expected.Spec.Versions[0].Schema.OpenAPIV3Schema.Properties["spec"].Properties + + result := map[string]string{} + for name, prop := range specProps { + result[name] = prop.Type + } + return result +} + +func readCRD(workingDir string) apiextensionsv1.CustomResourceDefinition { + path := filepath.Join(workingDir, "api.yaml") + if _, err := os.Stat(path); err != nil { + path = filepath.Join(workingDir, "promise.yaml") + } + return readCRDFromPath(path) +} + +func readCRDFromPath(path string) apiextensionsv1.CustomResourceDefinition { + data, err := os.ReadFile(path) + Expect(err).NotTo(HaveOccurred()) + crd := apiextensionsv1.CustomResourceDefinition{} + Expect(yaml.Unmarshal(data, &crd)).To(Succeed()) + return crd +}