diff --git a/e2e/tf/v4/provider.tf b/e2e/tf/v4/provider.tf index 5a1ae467..e81eeadf 100644 --- a/e2e/tf/v4/provider.tf +++ b/e2e/tf/v4/provider.tf @@ -60,3 +60,29 @@ variable "crowdstrike_customer_id" { type = string default = "" } + +# BYO IP Prefix variables for testing +# Set via TF_VAR_byo_ip_* environment variables or directly in terraform.tfvars +variable "byo_ip_cidr" { + description = "BYO IP CIDR notation for the prefix" + type = string + default = "2606:54c2:2::/48" +} + +variable "byo_ip_asn" { + description = "BYO IP ASN number" + type = string + default = "13335" +} + +variable "byo_ip_loa_document_id" { + description = "BYO IP LOA document ID" + type = string + default = "a6061274b61449d3bb4521aee2e90c95" +} + +variable "byo_ip_prefix_id" { + description = "BYO IP prefix ID (for v4 provider or migration tests)" + type = string + default = "b543f1a825f4474b88e945f164624412" +} diff --git a/integration/v4_to_v5/testdata/byo_ip_prefix/expected/byo_ip_prefix.tf b/integration/v4_to_v5/testdata/byo_ip_prefix/expected/byo_ip_prefix.tf new file mode 100644 index 00000000..9624079d --- /dev/null +++ b/integration/v4_to_v5/testdata/byo_ip_prefix/expected/byo_ip_prefix.tf @@ -0,0 +1,217 @@ +# Comprehensive Integration Tests for byo_ip_prefix Migration (v4 → v5) +# This file covers all v4 schema attributes and Terraform patterns +# Target: 15-30+ resource instances + +# ============================================================================ +# PATTERN 1-2: Variables & Locals +# ============================================================================ + +variable "cloudflare_account_id" { + description = "Cloudflare account ID" + type = string + # No default - must be provided +} + +variable "cloudflare_zone_id" { + description = "Cloudflare zone ID" + type = string + # No default - must be provided +} + +variable "cloudflare_domain" { + description = "Cloudflare domain for testing" + type = string +} + +variable "prefix_base" { + description = "Base prefix ID for testing" + type = string + default = "cftftest" +} + +variable "enable_optional" { + description = "Whether to create optional resources" + type = bool + default = true +} + +variable "advertisement_status" { + description = "Advertisement status for prefixes" + type = string + default = "on" +} + +locals { + name_prefix = "cftftest" + environment = "integration-test" + description_template = "BYO IP Prefix for ${local.environment}" + + prefix_map = { + prod = "cftftest-prod-001" + staging = "cftftest-staging-001" + dev = "cftftest-dev-001" + test = "cftftest-test-001" + } + + prefix_list = [ + "cftftest-list-001", + "cftftest-list-002", + "cftftest-list-003" + ] +} + +# ============================================================================ +# PATTERN 1: Basic Resources (3 instances) +# ============================================================================ + +# Instance 1: Minimal resource (only required fields) +resource "cloudflare_byo_ip_prefix" "minimal" { + account_id = var.cloudflare_account_id +} + +# Instance 2: Full resource with all optional fields +resource "cloudflare_byo_ip_prefix" "full" { + account_id = var.cloudflare_account_id + description = "Full BYO IP prefix with all fields" +} + +# Instance 3: Resource with variables +resource "cloudflare_byo_ip_prefix" "with_variables" { + account_id = var.cloudflare_account_id + description = local.description_template +} + +# ============================================================================ +# PATTERN 3: for_each with Maps (4 instances) +# ============================================================================ + +resource "cloudflare_byo_ip_prefix" "from_map" { + for_each = local.prefix_map + + account_id = var.cloudflare_account_id + description = "Prefix for ${each.key} environment" +} + +# ============================================================================ +# PATTERN 4: for_each with Sets (3 instances) +# ============================================================================ + +resource "cloudflare_byo_ip_prefix" "from_set" { + for_each = toset(local.prefix_list) + + account_id = var.cloudflare_account_id + description = "Prefix from set: ${each.value}" +} + +# ============================================================================ +# PATTERN 5: count-based Resources (3 instances) +# ============================================================================ + +resource "cloudflare_byo_ip_prefix" "with_count" { + count = 3 + + account_id = var.cloudflare_account_id + description = "Prefix number ${count.index + 1}" +} + +# ============================================================================ +# PATTERN 6: Conditional Resource Creation (2 instances) +# ============================================================================ + +resource "cloudflare_byo_ip_prefix" "conditional" { + count = var.enable_optional ? 1 : 0 + + account_id = var.cloudflare_account_id + description = "Conditional prefix" +} + +resource "cloudflare_byo_ip_prefix" "conditional_ternary" { + count = var.enable_optional ? 2 : 0 + + account_id = var.cloudflare_account_id + description = var.enable_optional ? "Enabled prefix ${count.index}" : null +} + +# ============================================================================ +# PATTERN 7: Cross-resource References (1 instance) +# ============================================================================ + +# Note: byo_ip_prefix is a standalone resource, so cross-references are limited +# But we can reference other instances +resource "cloudflare_byo_ip_prefix" "reference" { + account_id = cloudflare_byo_ip_prefix.minimal.account_id + description = "References minimal: ${cloudflare_byo_ip_prefix.minimal.prefix_id}" +} + +# ============================================================================ +# PATTERN 8: Lifecycle Meta-arguments (2 instances) +# ============================================================================ + +resource "cloudflare_byo_ip_prefix" "with_lifecycle" { + account_id = var.cloudflare_account_id + description = "Prefix with lifecycle rules" + + lifecycle { + create_before_destroy = true + ignore_changes = [description] + } +} + +resource "cloudflare_byo_ip_prefix" "prevent_destroy" { + account_id = var.cloudflare_account_id + description = "Protected prefix" + + lifecycle { + prevent_destroy = true + } +} + +# ============================================================================ +# PATTERN 9: Terraform Functions (3 instances) +# ============================================================================ + +resource "cloudflare_byo_ip_prefix" "with_join" { + account_id = var.cloudflare_account_id + description = join(" - ", ["BYO IP", local.environment, "test"]) +} + +resource "cloudflare_byo_ip_prefix" "with_format" { + account_id = var.cloudflare_account_id + description = format("Prefix for %s environment", local.environment) +} + +resource "cloudflare_byo_ip_prefix" "with_interpolation" { + account_id = var.cloudflare_account_id + description = "Prefix managed by ${local.name_prefix} for ${local.environment}" +} + +# ============================================================================ +# EDGE CASES (2 instances) +# ============================================================================ + +# Edge case 1: Empty string description (vs null) +resource "cloudflare_byo_ip_prefix" "empty_description" { + account_id = var.cloudflare_account_id + description = "" +} + +# Edge case 2: Advertisement off +resource "cloudflare_byo_ip_prefix" "advertisement_off" { + account_id = var.cloudflare_account_id + description = "Prefix with advertisement disabled" +} + +# ============================================================================ +# INSTANCE COUNT SUMMARY +# ============================================================================ +# Basic: 3 +# for_each maps: 4 (prod, staging, dev, test) +# for_each sets: 3 +# count-based: 3 +# conditional: 3 (1 + 2 when enabled) +# cross-reference: 1 +# lifecycle: 2 +# functions: 3 +# edge cases: 2 +# TOTAL: 24 instances (exceeds 15-30 target) +# ============================================================================ diff --git a/integration/v4_to_v5/testdata/byo_ip_prefix/input/byo_ip_prefix.tf b/integration/v4_to_v5/testdata/byo_ip_prefix/input/byo_ip_prefix.tf new file mode 100644 index 00000000..19d0c111 --- /dev/null +++ b/integration/v4_to_v5/testdata/byo_ip_prefix/input/byo_ip_prefix.tf @@ -0,0 +1,241 @@ +# Comprehensive Integration Tests for byo_ip_prefix Migration (v4 → v5) +# This file covers all v4 schema attributes and Terraform patterns +# Target: 15-30+ resource instances + +# ============================================================================ +# PATTERN 1-2: Variables & Locals +# ============================================================================ + +variable "cloudflare_account_id" { + description = "Cloudflare account ID" + type = string + # No default - must be provided +} + +variable "cloudflare_zone_id" { + description = "Cloudflare zone ID" + type = string + # No default - must be provided +} + +variable "cloudflare_domain" { + description = "Cloudflare domain for testing" + type = string +} + +variable "prefix_base" { + description = "Base prefix ID for testing" + type = string + default = "cftftest" +} + +variable "enable_optional" { + description = "Whether to create optional resources" + type = bool + default = true +} + +variable "advertisement_status" { + description = "Advertisement status for prefixes" + type = string + default = "on" +} + +locals { + name_prefix = "cftftest" + environment = "integration-test" + description_template = "BYO IP Prefix for ${local.environment}" + + prefix_map = { + prod = "cftftest-prod-001" + staging = "cftftest-staging-001" + dev = "cftftest-dev-001" + test = "cftftest-test-001" + } + + prefix_list = [ + "cftftest-list-001", + "cftftest-list-002", + "cftftest-list-003" + ] +} + +# ============================================================================ +# PATTERN 1: Basic Resources (3 instances) +# ============================================================================ + +# Instance 1: Minimal resource (only required fields) +resource "cloudflare_byo_ip_prefix" "minimal" { + account_id = var.cloudflare_account_id + prefix_id = "cftftest-minimal-001" +} + +# Instance 2: Full resource with all optional fields +resource "cloudflare_byo_ip_prefix" "full" { + account_id = var.cloudflare_account_id + prefix_id = "cftftest-full-001" + description = "Full BYO IP prefix with all fields" + advertisement = "on" +} + +# Instance 3: Resource with variables +resource "cloudflare_byo_ip_prefix" "with_variables" { + account_id = var.cloudflare_account_id + prefix_id = var.prefix_base + description = local.description_template + advertisement = var.advertisement_status +} + +# ============================================================================ +# PATTERN 3: for_each with Maps (4 instances) +# ============================================================================ + +resource "cloudflare_byo_ip_prefix" "from_map" { + for_each = local.prefix_map + + account_id = var.cloudflare_account_id + prefix_id = each.value + description = "Prefix for ${each.key} environment" + advertisement = each.key == "prod" ? "on" : "off" +} + +# ============================================================================ +# PATTERN 4: for_each with Sets (3 instances) +# ============================================================================ + +resource "cloudflare_byo_ip_prefix" "from_set" { + for_each = toset(local.prefix_list) + + account_id = var.cloudflare_account_id + prefix_id = each.value + description = "Prefix from set: ${each.value}" +} + +# ============================================================================ +# PATTERN 5: count-based Resources (3 instances) +# ============================================================================ + +resource "cloudflare_byo_ip_prefix" "with_count" { + count = 3 + + account_id = var.cloudflare_account_id + prefix_id = "cftftest-count-${count.index}" + description = "Prefix number ${count.index + 1}" + advertisement = count.index % 2 == 0 ? "on" : "off" +} + +# ============================================================================ +# PATTERN 6: Conditional Resource Creation (2 instances) +# ============================================================================ + +resource "cloudflare_byo_ip_prefix" "conditional" { + count = var.enable_optional ? 1 : 0 + + account_id = var.cloudflare_account_id + prefix_id = "cftftest-conditional-001" + description = "Conditional prefix" + advertisement = "off" +} + +resource "cloudflare_byo_ip_prefix" "conditional_ternary" { + count = var.enable_optional ? 2 : 0 + + account_id = var.cloudflare_account_id + prefix_id = "cftftest-conditional-${count.index + 1}" + description = var.enable_optional ? "Enabled prefix ${count.index}" : null +} + +# ============================================================================ +# PATTERN 7: Cross-resource References (1 instance) +# ============================================================================ + +# Note: byo_ip_prefix is a standalone resource, so cross-references are limited +# But we can reference other instances +resource "cloudflare_byo_ip_prefix" "reference" { + account_id = cloudflare_byo_ip_prefix.minimal.account_id + prefix_id = "cftftest-reference-001" + description = "References minimal: ${cloudflare_byo_ip_prefix.minimal.prefix_id}" +} + +# ============================================================================ +# PATTERN 8: Lifecycle Meta-arguments (2 instances) +# ============================================================================ + +resource "cloudflare_byo_ip_prefix" "with_lifecycle" { + account_id = var.cloudflare_account_id + prefix_id = "cftftest-lifecycle-001" + description = "Prefix with lifecycle rules" + advertisement = "on" + + lifecycle { + create_before_destroy = true + ignore_changes = [description] + } +} + +resource "cloudflare_byo_ip_prefix" "prevent_destroy" { + account_id = var.cloudflare_account_id + prefix_id = "cftftest-prevent-001" + description = "Protected prefix" + + lifecycle { + prevent_destroy = true + } +} + +# ============================================================================ +# PATTERN 9: Terraform Functions (3 instances) +# ============================================================================ + +resource "cloudflare_byo_ip_prefix" "with_join" { + account_id = var.cloudflare_account_id + prefix_id = "cftftest-join-001" + description = join(" - ", ["BYO IP", local.environment, "test"]) +} + +resource "cloudflare_byo_ip_prefix" "with_format" { + account_id = var.cloudflare_account_id + prefix_id = "cftftest-format-001" + description = format("Prefix for %s environment", local.environment) + advertisement = "on" +} + +resource "cloudflare_byo_ip_prefix" "with_interpolation" { + account_id = var.cloudflare_account_id + prefix_id = "${local.name_prefix}-prefix-001" + description = "Prefix managed by ${local.name_prefix} for ${local.environment}" +} + +# ============================================================================ +# EDGE CASES (2 instances) +# ============================================================================ + +# Edge case 1: Empty string description (vs null) +resource "cloudflare_byo_ip_prefix" "empty_description" { + account_id = var.cloudflare_account_id + prefix_id = "${local.name_prefix}-empty-desc-001" + description = "" +} + +# Edge case 2: Advertisement off +resource "cloudflare_byo_ip_prefix" "advertisement_off" { + account_id = var.cloudflare_account_id + prefix_id = "${local.name_prefix}-adv-off-001" + description = "Prefix with advertisement disabled" + advertisement = "off" +} + +# ============================================================================ +# INSTANCE COUNT SUMMARY +# ============================================================================ +# Basic: 3 +# for_each maps: 4 (prod, staging, dev, test) +# for_each sets: 3 +# count-based: 3 +# conditional: 3 (1 + 2 when enabled) +# cross-reference: 1 +# lifecycle: 2 +# functions: 3 +# edge cases: 2 +# TOTAL: 24 instances (exceeds 15-30 target) +# ============================================================================ diff --git a/integration/v4_to_v5/testdata/byo_ip_prefix/input/byo_ip_prefix_e2e.tf b/integration/v4_to_v5/testdata/byo_ip_prefix/input/byo_ip_prefix_e2e.tf new file mode 100644 index 00000000..e4bd8fcf --- /dev/null +++ b/integration/v4_to_v5/testdata/byo_ip_prefix/input/byo_ip_prefix_e2e.tf @@ -0,0 +1,42 @@ +# Terraform v4 to v5 Migration E2E Test - BYO IP Prefix +# +# Note: The v4 provider's Create function does not call the API to create a +# prefix. It sets the resource ID from prefix_id and reads the existing prefix. +# A real prefix ID must be provided via CLOUDFLARE_BYO_IP_PREFIX_ID. +# +# See: https://github.com/cloudflare/terraform-provider-cloudflare/blob/main/docs/resources/byo_ip_prefix.md + +# Variables passed from root module +variable "cloudflare_account_id" { + description = "Cloudflare account ID" + type = string +} + +variable "byo_ip_cidr" { + description = "BYO IP CIDR (e.g., 2606:54c2:3::/48)" + type = string +} + +variable "byo_ip_asn" { + description = "BYO IP ASN (e.g., 13335)" + type = string +} + +variable "byo_ip_loa_document_id" { + description = "LOA Document ID" + type = string +} + +variable "byo_ip_prefix_id" { + description = "Pre-existing BYO IP prefix ID" + type = string +} + +# Test instance: BYO IP prefix +# This resource will be imported, not created +resource "cloudflare_byo_ip_prefix" "test" { + account_id = var.cloudflare_account_id + prefix_id = var.byo_ip_prefix_id + description = "E2E migration test prefix" + advertisement = "on" +} diff --git a/internal/e2e-runner/env.go b/internal/e2e-runner/env.go index d7bf6ce8..cea07af4 100644 --- a/internal/e2e-runner/env.go +++ b/internal/e2e-runner/env.go @@ -24,10 +24,22 @@ type E2EEnv struct { CrowdstrikeClientSecret string CrowdstrikeAPIURL string CrowdstrikeCustomerID string + BYOIPCidr string + BYOIPASN string + BYOIPLOADocumentID string + BYOIPPrefixID string } // LoadEnv loads environment variables and validates required ones func LoadEnv(required []string) (*E2EEnv, error) { + // Helper function to get environment variable with default value + getEnvWithDefault := func(key, defaultValue string) string { + if value := os.Getenv(key); value != "" { + return value + } + return defaultValue + } + env := &E2EEnv{ AccountID: os.Getenv("CLOUDFLARE_ACCOUNT_ID"), ZoneID: os.Getenv("CLOUDFLARE_ZONE_ID"), @@ -40,6 +52,10 @@ func LoadEnv(required []string) (*E2EEnv, error) { CrowdstrikeClientSecret: os.Getenv("CLOUDFLARE_CROWDSTRIKE_CLIENT_SECRET"), CrowdstrikeAPIURL: os.Getenv("CLOUDFLARE_CROWDSTRIKE_API_URL"), CrowdstrikeCustomerID: os.Getenv("CLOUDFLARE_CROWDSTRIKE_CUSTOMER_ID"), + BYOIPCidr: getEnvWithDefault("CLOUDFLARE_BYO_IP_CIDR", "2606:54c2:2::/48"), + BYOIPASN: getEnvWithDefault("CLOUDFLARE_BYO_IP_ASN", "13335"), + BYOIPLOADocumentID: getEnvWithDefault("CLOUDFLARE_BYO_IP_LOA_DOCUMENT_ID", "a6061274b61449d3bb4521aee2e90c95"), + BYOIPPrefixID: getEnvWithDefault("CLOUDFLARE_BYO_IP_PREFIX_ID", "b543f1a825f4474b88e945f164624412"), } // Validate required variables @@ -68,6 +84,14 @@ func LoadEnv(required []string) (*E2EEnv, error) { value = env.CrowdstrikeAPIURL case "CLOUDFLARE_CROWDSTRIKE_CUSTOMER_ID": value = env.CrowdstrikeCustomerID + case "CLOUDFLARE_BYO_IP_CIDR": + value = env.BYOIPCidr + case "CLOUDFLARE_BYO_IP_ASN": + value = env.BYOIPASN + case "CLOUDFLARE_BYO_IP_LOA_DOCUMENT_ID": + value = env.BYOIPLOADocumentID + case "CLOUDFLARE_BYO_IP_PREFIX_ID": + value = env.BYOIPPrefixID default: return nil, fmt.Errorf("unknown environment variable: %s", varName) } diff --git a/internal/e2e-runner/import.go b/internal/e2e-runner/import.go index c03ab27e..09afa7c1 100644 --- a/internal/e2e-runner/import.go +++ b/internal/e2e-runner/import.go @@ -1,4 +1,5 @@ // import.go handles parsing import annotations and generating import blocks. +// import.go handles parsing import annotations and generating import blocks. // // This file provides functionality to: // - Parse import annotations from Terraform configuration files @@ -6,16 +7,18 @@ // - Support variable interpolation in import addresses // // Import annotations use the format: -// # tf-migrate:import-address=
-// resource "type" "name" { ... } +// +// # tf-migrate:import-address= +// resource "type" "name" { ... } // // Where can include variable references like ${var.cloudflare_account_id} // // The generated import blocks are placed in the root module's main.tf: -// import { -// to = module.