Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 36 additions & 12 deletions stackit/internal/services/cdn/cdn_acc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,19 @@ var instanceResource = map[string]string{
"dns_name": fmt.Sprintf("tf-acc-%s.stackit.gg", strings.Split(uuid.NewString(), "-")[0]),
}

func configResources(regions string, geofencingCountries []string) string {
func configResources(regions string, geofencingCountries []string, blockedCountries *string) string {
var quotedCountries []string
for _, country := range geofencingCountries {
quotedCountries = append(quotedCountries, fmt.Sprintf(`%q`, country))
}

geofencingList := strings.Join(quotedCountries, ",")

blockedCountriesConfig := ""
if blockedCountries != nil {
blockedCountriesConfig = fmt.Sprintf("blocked_countries = [%s]", *blockedCountries)
}

return fmt.Sprintf(`
%s

Expand All @@ -56,7 +62,7 @@ func configResources(regions string, geofencingCountries []string) string {
}
}
regions = [%s]
blocked_countries = [%s]
%s

optimizer = {
enabled = true
Expand All @@ -80,11 +86,11 @@ func configResources(regions string, geofencingCountries []string) string {
records = ["${stackit_cdn_distribution.distribution.domains[0].name}."]
}
`, testutil.CdnProviderConfig(), testutil.ProjectId, instanceResource["config_backend_origin_url"], instanceResource["config_backend_origin_url"], geofencingList,
regions, instanceResource["blocked_countries"], testutil.ProjectId, instanceResource["dns_name"],
regions, blockedCountriesConfig, testutil.ProjectId, instanceResource["dns_name"],
testutil.ProjectId, instanceResource["custom_domain_prefix"])
}

func configCustomDomainResources(regions, cert, key string, geofencingCountries []string) string {
func configCustomDomainResources(regions, cert, key string, geofencingCountries []string, blockedCountries *string) string {
return fmt.Sprintf(`
%s

Expand All @@ -97,10 +103,10 @@ func configCustomDomainResources(regions, cert, key string, geofencingCountries
private_key = %q
}
}
`, configResources(regions, geofencingCountries), cert, key)
`, configResources(regions, geofencingCountries, blockedCountries), cert, key)
}

func configDatasources(regions, cert, key string, geofencingCountries []string) string {
func configDatasources(regions, cert, key string, geofencingCountries []string, blockedCountries *string) string {
return fmt.Sprintf(`
%s

Expand All @@ -115,7 +121,7 @@ func configDatasources(regions, cert, key string, geofencingCountries []string)
name = stackit_cdn_custom_domain.custom_domain.name

}
`, configCustomDomainResources(regions, cert, key, geofencingCountries))
`, configCustomDomainResources(regions, cert, key, geofencingCountries, blockedCountries))
}
func makeCertAndKey(t *testing.T, organization string) (cert, key []byte) {
privateKey, err := rsa.GenerateKey(cryptoRand.Reader, 2048)
Expand Down Expand Up @@ -162,13 +168,17 @@ func TestAccCDNDistributionResource(t *testing.T) {

organization_updated := fmt.Sprintf("organization-updated-%s", uuid.NewString())
cert_updated, key_updated := makeCertAndKey(t, organization_updated)

// Helper for default blocked countries
defaultBlockedCountries := cdn.PtrString(instanceResource["blocked_countries"])

resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
CheckDestroy: testAccCheckCDNDistributionDestroy,
Steps: []resource.TestStep{
// Distribution Create
{
Config: configResources(instanceResource["config_regions"], geofencing),
Config: configResources(instanceResource["config_regions"], geofencing, defaultBlockedCountries),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet("stackit_cdn_distribution.distribution", "distribution_id"),
resource.TestCheckResourceAttrSet("stackit_cdn_distribution.distribution", "created_at"),
Expand Down Expand Up @@ -200,15 +210,15 @@ func TestAccCDNDistributionResource(t *testing.T) {
},
// Wait step, that confirms the CNAME record has "propagated"
{
Config: configResources(instanceResource["config_regions"], geofencing),
Config: configResources(instanceResource["config_regions"], geofencing, defaultBlockedCountries),
Check: func(_ *terraform.State) error {
_, err := blockUntilDomainResolves(fullDomainName)
return err
},
},
// Custom Domain Create
{
Config: configCustomDomainResources(instanceResource["config_regions"], string(cert), string(key), geofencing),
Config: configCustomDomainResources(instanceResource["config_regions"], string(cert), string(key), geofencing, defaultBlockedCountries),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("stackit_cdn_custom_domain.custom_domain", "status", "ACTIVE"),
resource.TestCheckResourceAttr("stackit_cdn_custom_domain.custom_domain", "name", fullDomainName),
Expand Down Expand Up @@ -262,7 +272,7 @@ func TestAccCDNDistributionResource(t *testing.T) {
},
// Data Source
{
Config: configDatasources(instanceResource["config_regions"], string(cert), string(key), geofencing),
Config: configDatasources(instanceResource["config_regions"], string(cert), string(key), geofencing, defaultBlockedCountries),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet("data.stackit_cdn_distribution.distribution", "distribution_id"),
resource.TestCheckResourceAttrSet("data.stackit_cdn_distribution.distribution", "created_at"),
Expand Down Expand Up @@ -301,7 +311,7 @@ func TestAccCDNDistributionResource(t *testing.T) {
},
// Update
{
Config: configCustomDomainResources(instanceResource["config_regions_updated"], string(cert_updated), string(key_updated), geofencing),
Config: configCustomDomainResources(instanceResource["config_regions_updated"], string(cert_updated), string(key_updated), geofencing, defaultBlockedCountries),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet("stackit_cdn_distribution.distribution", "distribution_id"),
resource.TestCheckResourceAttrSet("stackit_cdn_distribution.distribution", "created_at"),
Expand Down Expand Up @@ -330,6 +340,20 @@ func TestAccCDNDistributionResource(t *testing.T) {
resource.TestCheckResourceAttrPair("stackit_cdn_distribution.distribution", "project_id", "stackit_cdn_custom_domain.custom_domain", "project_id"),
),
},
// Bug Fix Verification: Omitted Field Handling
//
// This step verifies that omitting 'blocked_countries' from the Terraform configuration
// (by setting the pointer to nil) does not cause an "inconsistent result" error.
//
// Previously, omitting the field resulted in a 'null' config, but the API returned an
// empty list '[]', causing a state mismatch. The 'Default' modifier in the schema now
// ensures the missing config is treated as an empty list, matching the API response.
{
Config: configResources(instanceResource["config_regions"], geofencing, nil),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.blocked_countries.#", "0"),
),
},
},
})
}
Expand Down
19 changes: 19 additions & 0 deletions stackit/internal/services/cdn/distribution/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/listdefault"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
Expand Down Expand Up @@ -281,8 +282,18 @@ func (r *distributionResource) Schema(_ context.Context, _ resource.SchemaReques
},
"blocked_countries": schema.ListAttribute{
Optional: true,
Computed: true, // Required when using Default
Description: schemaDescriptions["config_blocked_countries"],
ElementType: types.StringType,
// The API returns an empty list for blocked_countries even if the field is omitted
// (null) in the request. This causes an "inconsistent result" error in Terraform
// because the config is null but the state is [].
//
// By setting a Default value of an empty list, we tell Terraform to treat a missing
// blocked_countries block in the HCL as if the user explicitly defined
// blocked_countries = []. This ensures the config (empty list) matches the
// API response (empty list).
Default: listdefault.StaticValue(types.ListValueMust(types.StringType, []attr.Value{})),
},
},
},
Expand Down Expand Up @@ -812,6 +823,14 @@ func toCreatePayload(ctx context.Context, model *Model) (*cdn.CreateDistribution
}

payload := &cdn.CreateDistributionPayload{
Backend: &cdn.CreateDistributionPayloadBackend{
HttpBackendCreate: &cdn.HttpBackendCreate{
OriginUrl: cfg.Backend.HttpBackend.OriginUrl,
OriginRequestHeaders: cfg.Backend.HttpBackend.OriginRequestHeaders,
Geofencing: cfg.Backend.HttpBackend.Geofencing,
Type: cfg.Backend.HttpBackend.Type,
},
},
IntentId: cdn.PtrString(uuid.NewString()),
Regions: cfg.Regions,
BlockedCountries: cfg.BlockedCountries,
Expand Down
26 changes: 26 additions & 0 deletions stackit/internal/services/cdn/distribution/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,19 @@ func TestToCreatePayload(t *testing.T) {
"happy_path": {
Input: modelFixture(),
Expected: &cdn.CreateDistributionPayload{
Backend: &cdn.CreateDistributionPayloadBackend{
HttpBackendCreate: &cdn.HttpBackendCreate{
OriginUrl: cdn.PtrString("https://www.mycoolapp.com"),
OriginRequestHeaders: &map[string]string{
"testHeader0": "testHeaderValue0",
"testHeader1": "testHeaderValue1",
},
Geofencing: &map[string][]string{
"https://de.mycoolapp.com": {"DE", "FR"},
},
Type: cdn.PtrString("http"),
},
},
Regions: &[]cdn.Region{"EU", "US"},
BlockedCountries: &[]string{"XX", "YY", "ZZ"},
},
Expand All @@ -77,6 +90,19 @@ func TestToCreatePayload(t *testing.T) {
})
}),
Expected: &cdn.CreateDistributionPayload{
Backend: &cdn.CreateDistributionPayloadBackend{
HttpBackendCreate: &cdn.HttpBackendCreate{
OriginUrl: cdn.PtrString("https://www.mycoolapp.com"),
OriginRequestHeaders: &map[string]string{
"testHeader0": "testHeaderValue0",
"testHeader1": "testHeaderValue1",
},
Geofencing: &map[string][]string{
"https://de.mycoolapp.com": {"DE", "FR"},
},
Type: cdn.PtrString("http"),
},
},
Regions: &[]cdn.Region{"EU", "US"},
Optimizer: cdn.NewOptimizer(true),
BlockedCountries: &[]string{"XX", "YY", "ZZ"},
Expand Down
Loading