Skip to content

Commit d96260e

Browse files
authored
feat(byo_ip_prefix): support migration (#131)
* feat(zero_trust_organization): support v4 to v5 migration * refactor imports to use import blocks generated in main.tf * update readme * feat(byo_ip_prefix): support migration * set up structure for pass through of byo ip prefix env vars, fix linter * add byo ip env vars, update prefix * fix int tests * update prefix consistency, import statements * move state logic to provider * update byo ip with warnings * skip byo ip e2e test * remove import in zero trust org * remove any changes to import * remove changes to runner * add missing func * remove e2e skip logic * exclude in ci
1 parent e57f2dd commit d96260e

13 files changed

Lines changed: 971 additions & 7 deletions

File tree

.github/workflows/e2e-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,4 @@ jobs:
5353

5454
- name: Run E2E Tests (provider state upgrader)
5555
working-directory: ./tf-migrate
56-
run: ./scripts/run-e2e-tests.sh --apply-exemptions --uses-provider-state-upgrader --provider ../provider --parallelism 5
56+
run: ./scripts/run-e2e-tests.sh --apply-exemptions --uses-provider-state-upgrader --provider ../provider --parallelism 5 --exclude byo_ip_prefix

agents.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,72 @@ import {
564564
}
565565
```
566566

567+
#### Skipping Resources from E2E Tests
568+
569+
Some resources cannot be tested in E2E environments due to lifecycle constraints (cannot be created/destroyed) or external dependencies. To exclude a resource from E2E tests while keeping integration tests:
570+
571+
**Add E2E-SKIP marker to the `*_e2e.tf` file:**
572+
573+
```hcl
574+
# E2E-SKIP: Brief reason why E2E testing is not possible
575+
#
576+
# REASON FOR SKIP:
577+
# - Detailed explanation of constraints
578+
# - Why automated testing is not feasible
579+
#
580+
# TESTING COVERAGE:
581+
# ✓ Integration tests: What IS tested
582+
# ✓ Provider tests: What IS tested
583+
# ✗ E2E tests: Why NOT tested
584+
585+
# Rest of file content...
586+
```
587+
588+
**Pattern Rules:**
589+
590+
1. **Marker location**: Must be in the first 20 lines of the `*_e2e.tf` file
591+
2. **Format**: `# E2E-SKIP:` (case-sensitive, must be in a comment)
592+
3. **Documentation**: Include detailed reasoning and alternative test coverage
593+
4. **Integration tests**: Continue to work normally (use separate input files)
594+
595+
**Example: BYO IP Prefix**
596+
597+
The `byo_ip_prefix` resource is skipped from E2E tests because:
598+
- Cannot be created via Terraform (requires manual account manager provisioning)
599+
- Cannot be destroyed (active bindings prevent deletion)
600+
- Requires manual intervention during migration
601+
602+
See: `integration/v4_to_v5/testdata/byo_ip_prefix/input/byo_ip_prefix_e2e.tf`
603+
604+
**E2E Runner Behavior:**
605+
606+
```bash
607+
# When initializing E2E tests:
608+
./bin/e2e-runner init
609+
610+
# Output shows skipped resources:
611+
Syncing resource files from testdata...
612+
✓ dns_record/dns_record.tf (from dns_record_e2e.tf)
613+
⊗ Skipped byo_ip_prefix (E2E-SKIP)
614+
✓ zone_setting/zone_setting.tf (from zone_setting_e2e.tf)
615+
616+
Total: 120 files synced
617+
Skipped: 1 modules (E2E-SKIP)
618+
```
619+
620+
**When to Use E2E-SKIP:**
621+
622+
Use the E2E-SKIP marker when:
623+
- Resources cannot be created via Terraform/API
624+
- Resources cannot be destroyed (lifecycle constraints)
625+
- Resources require manual provisioning or external setup
626+
- E2E testing would require pre-existing infrastructure
627+
628+
**Do NOT use E2E-SKIP for:**
629+
- Resources that can be imported (use import annotations instead)
630+
- Resources with slow operations (improve test efficiency instead)
631+
- Temporary test failures (fix the underlying issue)
632+
567633
---
568634

569635
## Drift Exemptions System
@@ -1196,6 +1262,50 @@ func (t *MyTransformer) TransformHCL(ctx *transform.Context) (*transform.Result,
11961262
}
11971263
```
11981264

1265+
### Pattern 7: Manual Intervention with Warning Comments
1266+
1267+
When required fields cannot be automatically populated (e.g., values must come from external sources), add warning comments to guide users:
1268+
1269+
```go
1270+
// See: internal/resources/byo_ip_prefix/v4_to_v5.go
1271+
// See: internal/resources/list_item/v4_to_v5.go
1272+
1273+
func (m *V4ToV5Migrator) TransformConfig(ctx *transform.Context, block *hclwrite.Block) (*transform.TransformResult, error) {
1274+
body := block.Body()
1275+
1276+
// Remove deprecated fields
1277+
tfhcl.RemoveAttributes(body, "old_field_1", "old_field_2")
1278+
1279+
// Add warning comment for fields requiring manual intervention
1280+
warningMsg := "This resource requires manual intervention to add v5 required fields 'field_a' and 'field_b'. Find values in [source]. See migration documentation for details."
1281+
tfhcl.AppendWarningComment(body, warningMsg)
1282+
1283+
return &transform.TransformResult{
1284+
Blocks: []*hclwrite.Block{block},
1285+
RemoveOriginal: false,
1286+
}, nil
1287+
}
1288+
```
1289+
1290+
**Result in HCL:**
1291+
```hcl
1292+
resource "cloudflare_example" "test" {
1293+
account_id = "abc123"
1294+
# MIGRATION WARNING: This resource requires manual intervention to add v5 required fields 'field_a' and 'field_b'. Find values in [source]. See migration documentation for details.
1295+
}
1296+
```
1297+
1298+
**When to use:**
1299+
- New v5 required fields don't exist in v4
1300+
- Values must come from external sources (API, dashboard, user)
1301+
- No reasonable default value exists
1302+
- Field values are account/environment-specific
1303+
1304+
**Integration test strategy:**
1305+
- Expected output files include the warning comment
1306+
- E2E tests simulate user adding fields from environment variables
1307+
- Provider tests verify warning exists before manual intervention step
1308+
11991309
---
12001310

12011311
## Additional Resources

e2e/tf/v4/provider.tf

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,29 @@ variable "crowdstrike_customer_id" {
6060
type = string
6161
default = ""
6262
}
63+
64+
# BYO IP Prefix variables for testing
65+
# Set via TF_VAR_byo_ip_* environment variables or directly in terraform.tfvars
66+
variable "byo_ip_cidr" {
67+
description = "BYO IP CIDR notation for the prefix"
68+
type = string
69+
default = "2606:54c2:2::/48"
70+
}
71+
72+
variable "byo_ip_asn" {
73+
description = "BYO IP ASN number"
74+
type = string
75+
default = "13335"
76+
}
77+
78+
variable "byo_ip_loa_document_id" {
79+
description = "BYO IP LOA document ID"
80+
type = string
81+
default = "a6061274b61449d3bb4521aee2e90c95"
82+
}
83+
84+
variable "byo_ip_prefix_id" {
85+
description = "BYO IP prefix ID (for v4 provider or migration tests)"
86+
type = string
87+
default = "b543f1a825f4474b88e945f164624412"
88+
}
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
# Comprehensive Integration Tests for byo_ip_prefix Migration (v4 → v5)
2+
# This file covers all v4 schema attributes and Terraform patterns
3+
# Target: 15-30+ resource instances
4+
5+
# ============================================================================
6+
# PATTERN 1-2: Variables & Locals
7+
# ============================================================================
8+
9+
variable "cloudflare_account_id" {
10+
description = "Cloudflare account ID"
11+
type = string
12+
# No default - must be provided
13+
}
14+
15+
variable "cloudflare_zone_id" {
16+
description = "Cloudflare zone ID"
17+
type = string
18+
# No default - must be provided
19+
}
20+
21+
variable "cloudflare_domain" {
22+
description = "Cloudflare domain for testing"
23+
type = string
24+
}
25+
26+
variable "prefix_base" {
27+
description = "Base prefix ID for testing"
28+
type = string
29+
default = "cftftest"
30+
}
31+
32+
variable "enable_optional" {
33+
description = "Whether to create optional resources"
34+
type = bool
35+
default = true
36+
}
37+
38+
variable "advertisement_status" {
39+
description = "Advertisement status for prefixes"
40+
type = string
41+
default = "on"
42+
}
43+
44+
locals {
45+
name_prefix = "cftftest"
46+
environment = "integration-test"
47+
description_template = "BYO IP Prefix for ${local.environment}"
48+
49+
prefix_map = {
50+
prod = "cftftest-prod-001"
51+
staging = "cftftest-staging-001"
52+
dev = "cftftest-dev-001"
53+
test = "cftftest-test-001"
54+
}
55+
56+
prefix_list = [
57+
"cftftest-list-001",
58+
"cftftest-list-002",
59+
"cftftest-list-003"
60+
]
61+
}
62+
63+
# ============================================================================
64+
# PATTERN 1: Basic Resources (3 instances)
65+
# ============================================================================
66+
67+
# Instance 1: Minimal resource (only required fields)
68+
resource "cloudflare_byo_ip_prefix" "minimal" {
69+
account_id = var.cloudflare_account_id
70+
# MIGRATION WARNING: This resource requires manual intervention to add v5 required fields 'asn' and 'cidr'. Find values in Cloudflare Dashboard → Manage Account → IP Addresses → IP Prefixes. See migration documentation for details.
71+
}
72+
73+
# Instance 2: Full resource with all optional fields
74+
resource "cloudflare_byo_ip_prefix" "full" {
75+
account_id = var.cloudflare_account_id
76+
description = "Full BYO IP prefix with all fields"
77+
# MIGRATION WARNING: This resource requires manual intervention to add v5 required fields 'asn' and 'cidr'. Find values in Cloudflare Dashboard → Manage Account → IP Addresses → IP Prefixes. See migration documentation for details.
78+
}
79+
80+
# Instance 3: Resource with variables
81+
resource "cloudflare_byo_ip_prefix" "with_variables" {
82+
account_id = var.cloudflare_account_id
83+
description = local.description_template
84+
# MIGRATION WARNING: This resource requires manual intervention to add v5 required fields 'asn' and 'cidr'. Find values in Cloudflare Dashboard → Manage Account → IP Addresses → IP Prefixes. See migration documentation for details.
85+
}
86+
87+
# ============================================================================
88+
# PATTERN 3: for_each with Maps (4 instances)
89+
# ============================================================================
90+
91+
resource "cloudflare_byo_ip_prefix" "from_map" {
92+
for_each = local.prefix_map
93+
94+
account_id = var.cloudflare_account_id
95+
description = "Prefix for ${each.key} environment"
96+
# MIGRATION WARNING: This resource requires manual intervention to add v5 required fields 'asn' and 'cidr'. Find values in Cloudflare Dashboard → Manage Account → IP Addresses → IP Prefixes. See migration documentation for details.
97+
}
98+
99+
# ============================================================================
100+
# PATTERN 4: for_each with Sets (3 instances)
101+
# ============================================================================
102+
103+
resource "cloudflare_byo_ip_prefix" "from_set" {
104+
for_each = toset(local.prefix_list)
105+
106+
account_id = var.cloudflare_account_id
107+
description = "Prefix from set: ${each.value}"
108+
# MIGRATION WARNING: This resource requires manual intervention to add v5 required fields 'asn' and 'cidr'. Find values in Cloudflare Dashboard → Manage Account → IP Addresses → IP Prefixes. See migration documentation for details.
109+
}
110+
111+
# ============================================================================
112+
# PATTERN 5: count-based Resources (3 instances)
113+
# ============================================================================
114+
115+
resource "cloudflare_byo_ip_prefix" "with_count" {
116+
count = 3
117+
118+
account_id = var.cloudflare_account_id
119+
description = "Prefix number ${count.index + 1}"
120+
# MIGRATION WARNING: This resource requires manual intervention to add v5 required fields 'asn' and 'cidr'. Find values in Cloudflare Dashboard → Manage Account → IP Addresses → IP Prefixes. See migration documentation for details.
121+
}
122+
123+
# ============================================================================
124+
# PATTERN 6: Conditional Resource Creation (2 instances)
125+
# ============================================================================
126+
127+
resource "cloudflare_byo_ip_prefix" "conditional" {
128+
count = var.enable_optional ? 1 : 0
129+
130+
account_id = var.cloudflare_account_id
131+
description = "Conditional prefix"
132+
# MIGRATION WARNING: This resource requires manual intervention to add v5 required fields 'asn' and 'cidr'. Find values in Cloudflare Dashboard → Manage Account → IP Addresses → IP Prefixes. See migration documentation for details.
133+
}
134+
135+
resource "cloudflare_byo_ip_prefix" "conditional_ternary" {
136+
count = var.enable_optional ? 2 : 0
137+
138+
account_id = var.cloudflare_account_id
139+
description = var.enable_optional ? "Enabled prefix ${count.index}" : null
140+
# MIGRATION WARNING: This resource requires manual intervention to add v5 required fields 'asn' and 'cidr'. Find values in Cloudflare Dashboard → Manage Account → IP Addresses → IP Prefixes. See migration documentation for details.
141+
}
142+
143+
# ============================================================================
144+
# PATTERN 7: Cross-resource References (1 instance)
145+
# ============================================================================
146+
147+
# Note: byo_ip_prefix is a standalone resource, so cross-references are limited
148+
# But we can reference other instances
149+
resource "cloudflare_byo_ip_prefix" "reference" {
150+
account_id = cloudflare_byo_ip_prefix.minimal.account_id
151+
description = "References minimal: ${cloudflare_byo_ip_prefix.minimal.prefix_id}"
152+
# MIGRATION WARNING: This resource requires manual intervention to add v5 required fields 'asn' and 'cidr'. Find values in Cloudflare Dashboard → Manage Account → IP Addresses → IP Prefixes. See migration documentation for details.
153+
}
154+
155+
# ============================================================================
156+
# PATTERN 8: Lifecycle Meta-arguments (2 instances)
157+
# ============================================================================
158+
159+
resource "cloudflare_byo_ip_prefix" "with_lifecycle" {
160+
account_id = var.cloudflare_account_id
161+
description = "Prefix with lifecycle rules"
162+
163+
lifecycle {
164+
create_before_destroy = true
165+
ignore_changes = [description]
166+
}
167+
# MIGRATION WARNING: This resource requires manual intervention to add v5 required fields 'asn' and 'cidr'. Find values in Cloudflare Dashboard → Manage Account → IP Addresses → IP Prefixes. See migration documentation for details.
168+
}
169+
170+
resource "cloudflare_byo_ip_prefix" "prevent_destroy" {
171+
account_id = var.cloudflare_account_id
172+
description = "Protected prefix"
173+
174+
lifecycle {
175+
prevent_destroy = true
176+
}
177+
# MIGRATION WARNING: This resource requires manual intervention to add v5 required fields 'asn' and 'cidr'. Find values in Cloudflare Dashboard → Manage Account → IP Addresses → IP Prefixes. See migration documentation for details.
178+
}
179+
180+
# ============================================================================
181+
# PATTERN 9: Terraform Functions (3 instances)
182+
# ============================================================================
183+
184+
resource "cloudflare_byo_ip_prefix" "with_join" {
185+
account_id = var.cloudflare_account_id
186+
description = join(" - ", ["BYO IP", local.environment, "test"])
187+
# MIGRATION WARNING: This resource requires manual intervention to add v5 required fields 'asn' and 'cidr'. Find values in Cloudflare Dashboard → Manage Account → IP Addresses → IP Prefixes. See migration documentation for details.
188+
}
189+
190+
resource "cloudflare_byo_ip_prefix" "with_format" {
191+
account_id = var.cloudflare_account_id
192+
description = format("Prefix for %s environment", local.environment)
193+
# MIGRATION WARNING: This resource requires manual intervention to add v5 required fields 'asn' and 'cidr'. Find values in Cloudflare Dashboard → Manage Account → IP Addresses → IP Prefixes. See migration documentation for details.
194+
}
195+
196+
resource "cloudflare_byo_ip_prefix" "with_interpolation" {
197+
account_id = var.cloudflare_account_id
198+
description = "Prefix managed by ${local.name_prefix} for ${local.environment}"
199+
# MIGRATION WARNING: This resource requires manual intervention to add v5 required fields 'asn' and 'cidr'. Find values in Cloudflare Dashboard → Manage Account → IP Addresses → IP Prefixes. See migration documentation for details.
200+
}
201+
202+
# ============================================================================
203+
# EDGE CASES (2 instances)
204+
# ============================================================================
205+
206+
# Edge case 1: Empty string description (vs null)
207+
resource "cloudflare_byo_ip_prefix" "empty_description" {
208+
account_id = var.cloudflare_account_id
209+
description = ""
210+
# MIGRATION WARNING: This resource requires manual intervention to add v5 required fields 'asn' and 'cidr'. Find values in Cloudflare Dashboard → Manage Account → IP Addresses → IP Prefixes. See migration documentation for details.
211+
}
212+
213+
# Edge case 2: Advertisement off
214+
resource "cloudflare_byo_ip_prefix" "advertisement_off" {
215+
account_id = var.cloudflare_account_id
216+
description = "Prefix with advertisement disabled"
217+
# MIGRATION WARNING: This resource requires manual intervention to add v5 required fields 'asn' and 'cidr'. Find values in Cloudflare Dashboard → Manage Account → IP Addresses → IP Prefixes. See migration documentation for details.
218+
}
219+
220+
# ============================================================================
221+
# INSTANCE COUNT SUMMARY
222+
# ============================================================================
223+
# Basic: 3
224+
# for_each maps: 4 (prod, staging, dev, test)
225+
# for_each sets: 3
226+
# count-based: 3
227+
# conditional: 3 (1 + 2 when enabled)
228+
# cross-reference: 1
229+
# lifecycle: 2
230+
# functions: 3
231+
# edge cases: 2
232+
# TOTAL: 24 instances (exceeds 15-30 target)
233+
# ============================================================================

0 commit comments

Comments
 (0)