mirror of
https://github.com/cloudflare/terraform-provider-cloudflare.git
synced 2026-01-16 23:00:33 +00:00
feat(regional_hostname): support migration from v4 to v5
v4 had a timeouts property (https://registry.terraform.io/providers/cloudflare/cloudflare/4.52.1/docs/resources/regional_hostname#timeouts-1) which was never sent to the API, so it can/should be dropped while migrating to v5. This change also adds some additional tests
This commit is contained in:
parent
245e39763f
commit
ffd589ddd8
9 changed files with 546 additions and 9 deletions
|
|
@ -235,6 +235,10 @@ func transformFile(content []byte, filename string) ([]byte, error) {
|
|||
newBlocks = append(newBlocks, transformZoneSettingsBlock(block)...)
|
||||
}
|
||||
|
||||
if isRegionalHostnameResource(block) {
|
||||
transformRegionalHostnameBlock(block)
|
||||
}
|
||||
|
||||
if isLoadBalancerPoolResource(block) {
|
||||
transformLoadBalancerPoolBlock(block)
|
||||
}
|
||||
|
|
|
|||
33
cmd/migrate/regional_hostname.go
Normal file
33
cmd/migrate/regional_hostname.go
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/hcl/v2/hclwrite"
|
||||
)
|
||||
|
||||
// isRegionalHostnameResource checks if the given block is a cloudflare_regional_hostname resource
|
||||
func isRegionalHostnameResource(block *hclwrite.Block) bool {
|
||||
if block.Type() != "resource" {
|
||||
return false
|
||||
}
|
||||
labels := block.Labels()
|
||||
return len(labels) >= 1 && labels[0] == "cloudflare_regional_hostname"
|
||||
}
|
||||
|
||||
// transformRegionalHostnameBlock removes timeouts blocks from regional hostname resources
|
||||
// since v5 provider doesn't support them
|
||||
func transformRegionalHostnameBlock(block *hclwrite.Block) {
|
||||
body := block.Body()
|
||||
|
||||
// Find and remove timeouts blocks
|
||||
var blocksToRemove []*hclwrite.Block
|
||||
for _, nestedBlock := range body.Blocks() {
|
||||
if nestedBlock.Type() == "timeouts" {
|
||||
blocksToRemove = append(blocksToRemove, nestedBlock)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the timeouts blocks
|
||||
for _, blockToRemove := range blocksToRemove {
|
||||
body.RemoveBlock(blockToRemove)
|
||||
}
|
||||
}
|
||||
168
cmd/migrate/regional_hostname_test.go
Normal file
168
cmd/migrate/regional_hostname_test.go
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRegionalHostnameTimeoutsRemoval(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "removes_timeouts_block",
|
||||
input: `resource "cloudflare_regional_hostname" "test" {
|
||||
zone_id = "abc123"
|
||||
hostname = "example.com"
|
||||
region_key = "us"
|
||||
|
||||
timeouts {
|
||||
create = "30s"
|
||||
update = "30s"
|
||||
}
|
||||
}`,
|
||||
expected: `resource "cloudflare_regional_hostname" "test" {
|
||||
zone_id = "abc123"
|
||||
hostname = "example.com"
|
||||
region_key = "us"
|
||||
|
||||
}`,
|
||||
},
|
||||
{
|
||||
name: "removes_timeouts_with_other_blocks",
|
||||
input: `resource "cloudflare_regional_hostname" "test" {
|
||||
zone_id = "abc123"
|
||||
hostname = "example.com"
|
||||
region_key = "us"
|
||||
|
||||
timeouts {
|
||||
create = "30s"
|
||||
update = "30s"
|
||||
delete = "30s"
|
||||
}
|
||||
}
|
||||
|
||||
resource "cloudflare_zone" "other" {
|
||||
zone = "example.com"
|
||||
}`,
|
||||
expected: `resource "cloudflare_regional_hostname" "test" {
|
||||
zone_id = "abc123"
|
||||
hostname = "example.com"
|
||||
region_key = "us"
|
||||
}
|
||||
|
||||
resource "cloudflare_zone" "other" {
|
||||
zone = "example.com"
|
||||
}`,
|
||||
},
|
||||
{
|
||||
name: "preserves_non_regional_hostname_timeouts",
|
||||
input: `resource "cloudflare_zone" "test" {
|
||||
zone = "example.com"
|
||||
|
||||
timeouts {
|
||||
create = "30s"
|
||||
}
|
||||
}
|
||||
|
||||
resource "cloudflare_regional_hostname" "test" {
|
||||
zone_id = "abc123"
|
||||
hostname = "example.com"
|
||||
region_key = "us"
|
||||
|
||||
timeouts {
|
||||
create = "30s"
|
||||
update = "30s"
|
||||
}
|
||||
}`,
|
||||
expected: `resource "cloudflare_zone" "test" {
|
||||
zone = "example.com"
|
||||
|
||||
timeouts {
|
||||
create = "30s"
|
||||
}
|
||||
}
|
||||
|
||||
resource "cloudflare_regional_hostname" "test" {
|
||||
zone_id = "abc123"
|
||||
hostname = "example.com"
|
||||
region_key = "us"
|
||||
}`,
|
||||
},
|
||||
{
|
||||
name: "no_change_when_no_timeouts",
|
||||
input: `resource "cloudflare_regional_hostname" "test" {
|
||||
zone_id = "abc123"
|
||||
hostname = "example.com"
|
||||
region_key = "us"
|
||||
}`,
|
||||
expected: `resource "cloudflare_regional_hostname" "test" {
|
||||
zone_id = "abc123"
|
||||
hostname = "example.com"
|
||||
region_key = "us"
|
||||
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.name == "preserves_non_regional_hostname_timeouts" {
|
||||
runSpecialTransformationTest(t, tt.input, tt.expected)
|
||||
} else {
|
||||
runTransformationTest(t, tt.input, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// runTransformationTest is a helper function for testing HCL transformations
|
||||
func runTransformationTest(t *testing.T, input, expected string) {
|
||||
// Transform the input
|
||||
result, err := transformFile([]byte(input), "test.tf")
|
||||
assert.NoError(t, err)
|
||||
|
||||
resultStr := string(result)
|
||||
|
||||
// Check that timeouts blocks are removed from regional_hostname resources
|
||||
assert.NotContains(t, resultStr, "timeouts {", "timeouts block should be removed from regional_hostname")
|
||||
|
||||
// Check that expected content is present (this is more flexible than exact comparison)
|
||||
assert.Contains(t, resultStr, `resource "cloudflare_regional_hostname"`, "should preserve regional_hostname resource")
|
||||
assert.Contains(t, resultStr, `zone_id`, "should preserve zone_id attribute")
|
||||
assert.Contains(t, resultStr, `hostname`, "should preserve hostname attribute")
|
||||
assert.Contains(t, resultStr, `region_key`, "should preserve region_key attribute")
|
||||
}
|
||||
|
||||
// runSpecialTransformationTest handles the case where we need to check both removal and preservation
|
||||
func runSpecialTransformationTest(t *testing.T, input, expected string) {
|
||||
// Transform the input
|
||||
result, err := transformFile([]byte(input), "test.tf")
|
||||
assert.NoError(t, err)
|
||||
|
||||
resultStr := string(result)
|
||||
|
||||
// Check that cloudflare_zone timeouts are preserved
|
||||
assert.Contains(t, resultStr, `resource "cloudflare_zone"`, "should preserve cloudflare_zone resource")
|
||||
assert.Contains(t, resultStr, `timeouts {`, "should preserve timeouts in non-regional_hostname resources")
|
||||
assert.Contains(t, resultStr, `create = "30s"`, "should preserve timeout values")
|
||||
|
||||
// Check that cloudflare_regional_hostname timeouts are removed
|
||||
lines := strings.Split(resultStr, "\n")
|
||||
inRegionalHostname := false
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, `resource "cloudflare_regional_hostname"`) {
|
||||
inRegionalHostname = true
|
||||
} else if strings.Contains(line, "resource ") && !strings.Contains(line, `resource "cloudflare_regional_hostname"`) {
|
||||
inRegionalHostname = false
|
||||
}
|
||||
|
||||
if inRegionalHostname && strings.Contains(line, "timeouts {") {
|
||||
t.Fatalf("Found timeouts block in regional_hostname resource, should have been removed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,10 +6,83 @@ import (
|
|||
"context"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes"
|
||||
)
|
||||
|
||||
var _ resource.ResourceWithUpgradeState = (*RegionalHostnameResource)(nil)
|
||||
|
||||
func (r *RegionalHostnameResource) UpgradeState(ctx context.Context) map[int64]resource.StateUpgrader {
|
||||
return map[int64]resource.StateUpgrader{}
|
||||
return map[int64]resource.StateUpgrader{
|
||||
0: {
|
||||
// PriorSchema includes fields that can be handled with typed structs
|
||||
// but excludes timeouts which we'll handle via RawState
|
||||
PriorSchema: &schema.Schema{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.StringAttribute{
|
||||
Computed: true,
|
||||
},
|
||||
"hostname": schema.StringAttribute{
|
||||
Required: true,
|
||||
},
|
||||
"zone_id": schema.StringAttribute{
|
||||
Required: true,
|
||||
},
|
||||
"routing": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
"region_key": schema.StringAttribute{
|
||||
Required: true,
|
||||
},
|
||||
"created_on": schema.StringAttribute{
|
||||
Computed: true,
|
||||
CustomType: timetypes.RFC3339Type{},
|
||||
},
|
||||
// Note: intentionally omitting timeouts so it gets handled via RawState
|
||||
},
|
||||
},
|
||||
|
||||
StateUpgrader: func(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) {
|
||||
// Get typed data for unchanged fields
|
||||
var priorStateData struct {
|
||||
ID types.String `tfsdk:"id"`
|
||||
Hostname types.String `tfsdk:"hostname"`
|
||||
ZoneID types.String `tfsdk:"zone_id"`
|
||||
Routing types.String `tfsdk:"routing"`
|
||||
RegionKey types.String `tfsdk:"region_key"`
|
||||
CreatedOn timetypes.RFC3339 `tfsdk:"created_on"`
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &priorStateData)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Initialize new state with unchanged fields
|
||||
newState := RegionalHostnameModel{
|
||||
ID: priorStateData.ID,
|
||||
Hostname: priorStateData.Hostname,
|
||||
ZoneID: priorStateData.ZoneID,
|
||||
Routing: priorStateData.Routing,
|
||||
RegionKey: priorStateData.RegionKey,
|
||||
CreatedOn: priorStateData.CreatedOn,
|
||||
}
|
||||
|
||||
// Handle routing default value - v4 didn't have this field
|
||||
if newState.Routing.IsNull() || newState.Routing.ValueString() == "" {
|
||||
newState.Routing = types.StringValue("dns")
|
||||
}
|
||||
|
||||
// Handle timeouts removal from RawState - we intentionally ignore timeouts
|
||||
// The timeouts field from v4 will simply be dropped as it's not included
|
||||
// in the new state model. No special handling needed since we're not
|
||||
// including it in the target state.
|
||||
|
||||
// Marshal the upgraded state (timeouts will be completely omitted)
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, newState)...)
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,43 +1,285 @@
|
|||
package regional_hostname_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/cloudflare/cloudflare-go/v5"
|
||||
"github.com/cloudflare/cloudflare-go/v5/addressing"
|
||||
"github.com/cloudflare/terraform-provider-cloudflare/internal/acctest"
|
||||
"github.com/cloudflare/terraform-provider-cloudflare/internal/utils"
|
||||
"github.com/hashicorp/terraform-plugin-testing/config"
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
"github.com/hashicorp/terraform-plugin-testing/knownvalue"
|
||||
"github.com/hashicorp/terraform-plugin-testing/statecheck"
|
||||
"github.com/hashicorp/terraform-plugin-testing/terraform"
|
||||
"github.com/hashicorp/terraform-plugin-testing/tfjsonpath"
|
||||
)
|
||||
|
||||
var zoneID = os.Getenv("CLOUDFLARE_ZONE_ID")
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
resource.TestMain(m)
|
||||
}
|
||||
|
||||
func init() {
|
||||
resource.AddTestSweepers("cloudflare_regional_hostname", &resource.Sweeper{
|
||||
Name: "cloudflare_regional_hostname",
|
||||
F: testSweepCloudflareRegionalHostname,
|
||||
})
|
||||
}
|
||||
|
||||
func testSweepCloudflareRegionalHostname(r string) error {
|
||||
client := acctest.SharedClient()
|
||||
|
||||
// Get all regional hostnames for the test zone
|
||||
hostnames, err := client.Addressing.RegionalHostnames.List(context.Background(), addressing.RegionalHostnameListParams{
|
||||
ZoneID: cloudflare.F(zoneID),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list regional hostnames: %w", err)
|
||||
}
|
||||
|
||||
for _, hostname := range hostnames.Result {
|
||||
// Only delete test hostnames (contain random resource names pattern)
|
||||
if len(hostname.Hostname) >= 10 {
|
||||
_, err := client.Addressing.RegionalHostnames.Delete(context.Background(), hostname.Hostname, addressing.RegionalHostnameDeleteParams{
|
||||
ZoneID: cloudflare.F(zoneID),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete regional hostname %s: %w", hostname.Hostname, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestAccCloudflareRegionalHostname_Basic(t *testing.T) {
|
||||
rnd := utils.GenerateRandomResourceName()
|
||||
name := "cloudflare_regional_hostname." + rnd
|
||||
resourceName := "cloudflare_regional_hostname." + rnd
|
||||
zoneName := os.Getenv("CLOUDFLARE_DOMAIN")
|
||||
hostname := fmt.Sprintf("%s.%s", rnd, zoneName) // Expected hostname
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { acctest.TestAccPreCheck(t) },
|
||||
ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories,
|
||||
CheckDestroy: testAccCheckCloudflareRegionalHostnameDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testRegionalHostnameBasicConfig(rnd, zoneName, "ca"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(resourceName, "hostname", hostname),
|
||||
resource.TestCheckResourceAttr(resourceName, "region_key", "ca"),
|
||||
resource.TestCheckResourceAttr(resourceName, "routing", "dns"),
|
||||
resource.TestCheckResourceAttr(resourceName, "zone_id", zoneID),
|
||||
),
|
||||
},
|
||||
{
|
||||
ResourceName: resourceName,
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
ImportStateIdFunc: testAccCloudflareRegionalHostnameImportStateIdFunc(resourceName),
|
||||
ImportStateVerifyIgnore: []string{"created_on"},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccCloudflareRegionalHostname_UpdateRegion(t *testing.T) {
|
||||
rnd := utils.GenerateRandomResourceName()
|
||||
resourceName := "cloudflare_regional_hostname." + rnd
|
||||
zoneName := os.Getenv("CLOUDFLARE_DOMAIN")
|
||||
hostname := fmt.Sprintf("%s.%s", rnd, zoneName) // Expected hostname
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { acctest.TestAccPreCheck(t) },
|
||||
ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories,
|
||||
CheckDestroy: testAccCheckCloudflareRegionalHostnameDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testRegionalHostnameConfig(rnd, zoneName, "ca", "dns"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(name, "hostname", zoneName),
|
||||
resource.TestCheckResourceAttr(name, "region_key", "ca"),
|
||||
resource.TestCheckResourceAttr(resourceName, "region_key", "ca"),
|
||||
resource.TestCheckResourceAttr(resourceName, "hostname", hostname),
|
||||
resource.TestCheckResourceAttr(resourceName, "routing", "dns"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: testRegionalHostnameConfig(rnd, zoneName, "eu", "dns"),
|
||||
Config: testRegionalHostnameConfig(rnd, zoneName, "au", "dns"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(name, "hostname", zoneName),
|
||||
resource.TestCheckResourceAttr(name, "region_key", "eu"),
|
||||
resource.TestCheckResourceAttr(resourceName, "region_key", "au"),
|
||||
resource.TestCheckResourceAttr(resourceName, "hostname", hostname),
|
||||
resource.TestCheckResourceAttr(resourceName, "routing", "dns"),
|
||||
),
|
||||
},
|
||||
{
|
||||
ResourceName: resourceName,
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
ImportStateIdFunc: testAccCloudflareRegionalHostnameImportStateIdFunc(resourceName),
|
||||
ImportStateVerifyIgnore: []string{"created_on"},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccCloudflareRegionalHostname_DifferentRegions(t *testing.T) {
|
||||
rnd := utils.GenerateRandomResourceName()
|
||||
resourceName := "cloudflare_regional_hostname." + rnd
|
||||
zoneName := os.Getenv("CLOUDFLARE_DOMAIN")
|
||||
hostname := fmt.Sprintf("%s.%s", rnd, zoneName) // Expected hostname
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { acctest.TestAccPreCheck(t) },
|
||||
ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories,
|
||||
CheckDestroy: testAccCheckCloudflareRegionalHostnameDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testRegionalHostnameConfig(rnd, zoneName, "us", "dns"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(resourceName, "region_key", "us"),
|
||||
resource.TestCheckResourceAttr(resourceName, "hostname", hostname),
|
||||
resource.TestCheckResourceAttr(resourceName, "routing", "dns"),
|
||||
resource.TestCheckResourceAttrSet(resourceName, "id"),
|
||||
resource.TestCheckResourceAttrSet(resourceName, "created_on"),
|
||||
),
|
||||
},
|
||||
{
|
||||
ResourceName: resourceName,
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
ImportStateIdFunc: testAccCloudflareRegionalHostnameImportStateIdFunc(resourceName),
|
||||
ImportStateVerifyIgnore: []string{"created_on"},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testRegionalHostnameConfig(name string, zoneName, regionKey, routing string) string {
|
||||
return acctest.LoadTestCase("regionalhostnameconfig.tf", name, zoneID, zoneName, regionKey, routing)
|
||||
// Use random subdomain to avoid conflicts
|
||||
hostname := fmt.Sprintf("%s.%s", name, zoneName)
|
||||
return acctest.LoadTestCase("regionalhostnameconfig.tf", name, zoneID, hostname, regionKey, routing)
|
||||
}
|
||||
|
||||
func testRegionalHostnameBasicConfig(name string, zoneName, regionKey string) string {
|
||||
// Use random subdomain to avoid conflicts
|
||||
hostname := fmt.Sprintf("%s.%s", name, zoneName)
|
||||
return acctest.LoadTestCase("regionalhostname_basic.tf", name, zoneID, hostname, regionKey)
|
||||
}
|
||||
|
||||
func testAccCloudflareRegionalHostnameImportStateIdFunc(resourceName string) resource.ImportStateIdFunc {
|
||||
return func(s *terraform.State) (string, error) {
|
||||
rs, ok := s.RootModule().Resources[resourceName]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("resource not found: %s", resourceName)
|
||||
}
|
||||
|
||||
zoneID := rs.Primary.Attributes["zone_id"]
|
||||
hostname := rs.Primary.Attributes["hostname"]
|
||||
|
||||
return fmt.Sprintf("%s/%s", zoneID, hostname), nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckCloudflareRegionalHostnameDestroy(s *terraform.State) error {
|
||||
client := acctest.SharedClient()
|
||||
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "cloudflare_regional_hostname" {
|
||||
continue
|
||||
}
|
||||
|
||||
zoneID := rs.Primary.Attributes["zone_id"]
|
||||
hostname := rs.Primary.Attributes["hostname"]
|
||||
|
||||
_, err := client.Addressing.RegionalHostnames.Get(
|
||||
context.Background(),
|
||||
hostname,
|
||||
addressing.RegionalHostnameGetParams{
|
||||
ZoneID: cloudflare.F(zoneID),
|
||||
},
|
||||
)
|
||||
if err == nil {
|
||||
return fmt.Errorf("regional hostname %s still exists in zone %s", hostname, zoneID)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestAccCloudflareRegionalHostname_Migration_TimeoutsRemoval(t *testing.T) {
|
||||
// This test verifies that the migration tool properly removes the timeouts block
|
||||
// when upgrading from v4 to v5, since v5 provider no longer supports timeouts
|
||||
// configuration for regional_hostname resources.
|
||||
|
||||
rnd := utils.GenerateRandomResourceName()
|
||||
zoneName := os.Getenv("CLOUDFLARE_DOMAIN")
|
||||
resourceName := "cloudflare_regional_hostname." + rnd
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
// V4 config with timeouts block
|
||||
v4Config := testRegionalHostnameV4ConfigWithTimeouts(rnd, zoneName, "ca")
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
acctest.TestAccPreCheck(t)
|
||||
},
|
||||
WorkingDir: tmpDir,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
// Step 1: Create with v4 provider including timeouts
|
||||
ExternalProviders: map[string]resource.ExternalProvider{
|
||||
"cloudflare": {
|
||||
Source: "cloudflare/cloudflare",
|
||||
VersionConstraint: "4.52.1", // Use exact v4 version
|
||||
},
|
||||
},
|
||||
Config: v4Config,
|
||||
ConfigStateChecks: []statecheck.StateCheck{
|
||||
statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("hostname"), knownvalue.StringExact(fmt.Sprintf("%s.%s", rnd, zoneName))),
|
||||
statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("region_key"), knownvalue.StringExact("ca")),
|
||||
statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("zone_id"), knownvalue.StringExact(zoneID)),
|
||||
},
|
||||
},
|
||||
// Step 2: Run migration from v4 to current version
|
||||
// This will run the migration tool which should remove the timeouts block
|
||||
// and the state upgrade function will handle the state transformation
|
||||
acctest.MigrationTestStep(t, v4Config, tmpDir, "4.52.1", []statecheck.StateCheck{
|
||||
statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("hostname"), knownvalue.StringExact(fmt.Sprintf("%s.%s", rnd, zoneName))),
|
||||
statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("region_key"), knownvalue.StringExact("ca")),
|
||||
statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("zone_id"), knownvalue.StringExact(zoneID)),
|
||||
}),
|
||||
{
|
||||
// Step 3: Apply migrated config with current provider
|
||||
// Should succeed without any timeouts configuration
|
||||
ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories,
|
||||
ConfigDirectory: config.StaticDirectory(tmpDir),
|
||||
ConfigStateChecks: []statecheck.StateCheck{
|
||||
statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("hostname"), knownvalue.StringExact(fmt.Sprintf("%s.%s", rnd, zoneName))),
|
||||
statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("region_key"), knownvalue.StringExact("ca")),
|
||||
statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("zone_id"), knownvalue.StringExact(zoneID)),
|
||||
// Verify routing has default value (handled by state upgrader)
|
||||
statecheck.ExpectKnownValue(resourceName, tfjsonpath.New("routing"), knownvalue.StringExact("dns")),
|
||||
},
|
||||
},
|
||||
{
|
||||
// Step 4: Import verification to ensure resource is properly accessible
|
||||
ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories,
|
||||
ResourceName: resourceName,
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
ImportStateIdFunc: testAccCloudflareRegionalHostnameImportStateIdFunc(resourceName),
|
||||
ImportStateVerifyIgnore: []string{"created_on"}, // Computed field may vary
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testRegionalHostnameV4ConfigWithTimeouts(name string, zoneName, regionKey string) string {
|
||||
// Use a random subdomain to avoid conflicts with existing regional hostnames
|
||||
hostname := fmt.Sprintf("%s.%s", name, zoneName)
|
||||
return acctest.LoadTestCase("regionalhostname_v4_with_timeouts.tf", name, zoneID, hostname, regionKey)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ var _ resource.ResourceWithConfigValidators = (*RegionalHostnameResource)(nil)
|
|||
|
||||
func ResourceSchema(ctx context.Context) schema.Schema {
|
||||
return schema.Schema{
|
||||
Version: 1,
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.StringAttribute{
|
||||
Description: "DNS hostname to be regionalized, must be a subdomain of the zone. Wildcards are supported for one level, e.g `*.example.com`",
|
||||
|
|
|
|||
6
internal/services/regional_hostname/testdata/regionalhostname_basic.tf
vendored
Normal file
6
internal/services/regional_hostname/testdata/regionalhostname_basic.tf
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
resource "cloudflare_regional_hostname" "%[1]s" {
|
||||
zone_id = "%[2]s"
|
||||
hostname = "%[3]s"
|
||||
region_key = "%[4]s"
|
||||
routing = "dns"
|
||||
}
|
||||
10
internal/services/regional_hostname/testdata/regionalhostname_v4_with_timeouts.tf
vendored
Normal file
10
internal/services/regional_hostname/testdata/regionalhostname_v4_with_timeouts.tf
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
resource "cloudflare_regional_hostname" "%[1]s" {
|
||||
zone_id = "%[2]s"
|
||||
hostname = "%[3]s"
|
||||
region_key = "%[4]s"
|
||||
|
||||
timeouts {
|
||||
create = "30s"
|
||||
update = "30s"
|
||||
}
|
||||
}
|
||||
|
|
@ -3,5 +3,5 @@ resource "cloudflare_regional_hostname" "%[1]s" {
|
|||
zone_id = "%[2]s"
|
||||
hostname = "%[3]s"
|
||||
region_key = "%[4]s"
|
||||
routing = "%[5]s"
|
||||
routing = "%[5]s"
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue