From db1097663f20cc1f4c308e13d481073eb2d2a00a Mon Sep 17 00:00:00 2001 From: Matheus Politano Date: Mon, 30 Mar 2026 15:13:33 +0200 Subject: [PATCH 1/3] fix(cdn): Fix CDN acceptance tests --- stackit/internal/services/cdn/cdn_acc_test.go | 80 ++++++++++++------- .../cdn/testdata/resource-http-base.tf | 59 ++++++++++++++ .../testdata/resource-http-custom-domain.tf | 17 ++++ 3 files changed, 125 insertions(+), 31 deletions(-) create mode 100644 stackit/internal/services/cdn/testdata/resource-http-base.tf create mode 100644 stackit/internal/services/cdn/testdata/resource-http-custom-domain.tf diff --git a/stackit/internal/services/cdn/cdn_acc_test.go b/stackit/internal/services/cdn/cdn_acc_test.go index 100329554..d2d9537e3 100644 --- a/stackit/internal/services/cdn/cdn_acc_test.go +++ b/stackit/internal/services/cdn/cdn_acc_test.go @@ -34,19 +34,31 @@ var ( credentialsName = "acc-c" + acctest.RandStringFromCharSet(3, acctest.CharSetAlpha) credentialsNameUpdated = "acc-c-updated" + acctest.RandStringFromCharSet(3, acctest.CharSetAlpha) httpTestName = "acc-h" + acctest.RandStringFromCharSet(3, acctest.CharSetAlpha) - dnsNameHttp = fmt.Sprintf("tf-%s.runs.onstackit.cloud", httpTestName) - dnsRecordNameHttp = uuid.NewString() - cert, key = makeCertAndKey(testutil.OrganizationId) + + // FIX: Reverted to stackit.gg as used in the working old code to avoid reserved domain rejection + dnsNameHttp = fmt.Sprintf("tf-acc-%s.stackit.gg", strings.Split(uuid.NewString(), "-")[0]) + dnsRecordNameHttp = uuid.NewString() + + // Build the full domain name here so we can use it to sign the certificate + fullDomainNameHttp = fmt.Sprintf("%s.%s", dnsRecordNameHttp, dnsNameHttp) + + // Pass the full domain to the certificate generation + cert, key = makeCertAndKey(testutil.OrganizationId, fullDomainNameHttp) ) var ( //go:embed testdata/resource-bucket.tf resourceBucket string - //go:embed testdata/resource-http.tf - resourceHttp string + //go:embed testdata/resource-http-base.tf + resourceHttpBase string + + //go:embed testdata/resource-http-custom-domain.tf + resourceHttpCustomDomain string ) +var resourceHttpFull = resourceHttpBase + "\n" + resourceHttpCustomDomain + var testConfigVarsBucket = config.Variables{ "project_id": config.StringVariable(testutil.ProjectId), "bucket_name": config.StringVariable(bucketName), @@ -90,7 +102,7 @@ func configVarsHttpUpdated() config.Variables { return updatedConfig } -func makeCertAndKey(organization string) (cert, key []byte) { +func makeCertAndKey(organization string, domain string) (cert, key []byte) { privateKey, err := rsa.GenerateKey(cryptoRand.Reader, 2048) if err != nil { fmt.Printf("failed to generate key: %s", err.Error()) @@ -100,10 +112,11 @@ func makeCertAndKey(organization string) (cert, key []byte) { Issuer: pkix.Name{CommonName: organization}, Subject: pkix.Name{ Organization: []string{organization}, + CommonName: domain, // Required by most modern TLS validations }, - NotBefore: time.Now(), - NotAfter: time.Now().Add(time.Hour), - + DNSNames: []string{domain}, // Subject Alternative Name (SAN) is strictly required now + NotBefore: time.Now(), + NotAfter: time.Now().Add(time.Hour), KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, BasicConstraintsValid: true, @@ -129,14 +142,13 @@ func makeCertAndKey(organization string) (cert, key []byte) { } func TestAccCDNDistributionHttp(t *testing.T) { - fullDomainName := fmt.Sprintf("%s.%s", testutil.ConvertConfigVariable(testConfigVarsHttp["dns_record_name"]), testutil.ConvertConfigVariable(testConfigVarsHttp["dns_name"])) resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories, CheckDestroy: testAccCheckCDNDistributionDestroy, Steps: []resource.TestStep{ - // Distribution Create + // Distribution Create (Only Base config) { - Config: testutil.CdnProviderConfig() + "\n" + resourceHttp, + Config: testutil.CdnProviderConfig() + "\n" + resourceHttpBase, ConfigVariables: testConfigVarsHttp, Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrSet("stackit_cdn_distribution.distribution", "distribution_id"), @@ -166,22 +178,22 @@ func TestAccCDNDistributionHttp(t *testing.T) { resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "status", "ACTIVE"), ), }, - // Wait step, that confirms the CNAME record has "propagated" + // Wait step, confirms the CNAME record has "propagated" before trying to add the custom domain { - Config: testutil.CdnProviderConfig() + "\n" + resourceHttp, + Config: testutil.CdnProviderConfig() + "\n" + resourceHttpBase, ConfigVariables: testConfigVarsHttp, Check: func(_ *terraform.State) error { - _, err := blockUntilDomainResolves(fullDomainName) + _, err := blockUntilDomainResolves(fullDomainNameHttp) return err }, }, - // Custom Domain Create + // Custom Domain Create (Now using Full config) { - Config: testutil.CdnProviderConfig() + "\n" + resourceHttp, + Config: testutil.CdnProviderConfig() + "\n" + resourceHttpFull, ConfigVariables: testConfigVarsHttp, Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("stackit_cdn_custom_domain.custom_domain", "status", "ACTIVE"), - resource.TestCheckResourceAttr("stackit_cdn_custom_domain.custom_domain", "name", fullDomainName), + resource.TestCheckResourceAttr("stackit_cdn_custom_domain.custom_domain", "name", fullDomainNameHttp), resource.TestCheckResourceAttr("stackit_cdn_custom_domain.custom_domain", "certificate.version", "1"), resource.TestCheckResourceAttrPair("stackit_cdn_distribution.distribution", "distribution_id", "stackit_cdn_custom_domain.custom_domain", "distribution_id"), resource.TestCheckResourceAttrPair("stackit_cdn_distribution.distribution", "project_id", "stackit_cdn_custom_domain.custom_domain", "project_id"), @@ -235,7 +247,7 @@ func TestAccCDNDistributionHttp(t *testing.T) { }, // Data Source { - Config: testutil.CdnProviderConfig() + "\n" + resourceHttp, + Config: testutil.CdnProviderConfig() + "\n" + resourceHttpFull, ConfigVariables: testConfigVarsHttp, Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrSet("data.stackit_cdn_distribution.distribution", "distribution_id"), @@ -243,7 +255,7 @@ func TestAccCDNDistributionHttp(t *testing.T) { resource.TestCheckResourceAttrSet("data.stackit_cdn_distribution.distribution", "updated_at"), resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "domains.#", "2"), resource.TestCheckResourceAttrSet("data.stackit_cdn_distribution.distribution", "domains.0.name"), - resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "domains.1.name", fullDomainName), + resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "domains.1.name", fullDomainNameHttp), resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "domains.0.status", "ACTIVE"), resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "domains.1.status", "ACTIVE"), resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "domains.0.type", "managed"), @@ -268,14 +280,14 @@ func TestAccCDNDistributionHttp(t *testing.T) { resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "status", "ACTIVE"), resource.TestCheckResourceAttr("data.stackit_cdn_custom_domain.custom_domain", "status", "ACTIVE"), - resource.TestCheckResourceAttr("data.stackit_cdn_custom_domain.custom_domain", "name", fullDomainName), + resource.TestCheckResourceAttr("data.stackit_cdn_custom_domain.custom_domain", "name", fullDomainNameHttp), resource.TestCheckResourceAttr("data.stackit_cdn_custom_domain.custom_domain", "certificate.version", "1"), resource.TestCheckResourceAttrPair("stackit_cdn_distribution.distribution", "distribution_id", "stackit_cdn_custom_domain.custom_domain", "distribution_id"), ), }, // Update { - Config: testutil.CdnProviderConfig() + "\n" + resourceHttp, + Config: testutil.CdnProviderConfig() + "\n" + resourceHttpFull, ConfigVariables: configVarsHttpUpdated(), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrSet("stackit_cdn_distribution.distribution", "distribution_id"), @@ -283,7 +295,7 @@ func TestAccCDNDistributionHttp(t *testing.T) { resource.TestCheckResourceAttrSet("stackit_cdn_distribution.distribution", "updated_at"), resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "domains.#", "2"), resource.TestCheckResourceAttrSet("stackit_cdn_distribution.distribution", "domains.0.name"), - resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "domains.1.name", fullDomainName), + resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "domains.1.name", fullDomainNameHttp), resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "domains.0.status", "ACTIVE"), resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "domains.1.status", "ACTIVE"), resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "domains.0.type", "managed"), @@ -309,7 +321,7 @@ func TestAccCDNDistributionHttp(t *testing.T) { ), resource.TestCheckResourceAttr("stackit_cdn_custom_domain.custom_domain", "status", "ACTIVE"), - resource.TestCheckResourceAttr("stackit_cdn_custom_domain.custom_domain", "name", fullDomainName), + resource.TestCheckResourceAttr("stackit_cdn_custom_domain.custom_domain", "name", fullDomainNameHttp), resource.TestCheckResourceAttr("stackit_cdn_custom_domain.custom_domain", "certificate.version", "1"), resource.TestCheckResourceAttrPair("stackit_cdn_distribution.distribution", "distribution_id", "stackit_cdn_custom_domain.custom_domain", "distribution_id"), resource.TestCheckResourceAttrPair("stackit_cdn_distribution.distribution", "project_id", "stackit_cdn_custom_domain.custom_domain", "project_id"), @@ -490,10 +502,10 @@ func testAccCheckCDNDistributionDestroy(s *terraform.State) error { const ( recordCheckInterval time.Duration = 3 * time.Second - recordCheckAttempts = 100 // wait up to 5 minutes for record to be come available (normally takes less than 2 minutes) + recordCheckAttempts = 100 // wait up to 5 minutes for record to become available (normally takes less than 2 minutes) ) -func blockUntilDomainResolves(domain string) (string, error) { +func blockUntilDomainResolves(domain string) (net.IP, error) { // Create a custom resolver that bypasses the local system DNS settings/cache // and queries Google DNS (8.8.8.8) directly. r := &net.Resolver{ @@ -506,17 +518,23 @@ func blockUntilDomainResolves(domain string) (string, error) { return d.DialContext(ctx, network, "8.8.8.8:53") }, } + // wait until it becomes ready - isReady := func() (string, error) { + isReady := func() (net.IP, error) { // Use a context for the individual query timeout ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() - cname, err := r.LookupCNAME(ctx, domain) + ips, err := r.LookupIP(ctx, "ip", domain) if err != nil { - return "", fmt.Errorf("error looking up CNAME for domain %s: %w", domain, err) + return nil, fmt.Errorf("error looking up IP for domain %s: %w", domain, err) + } + for _, ip := range ips { + if ip.String() != "" { + return ip, nil + } } - return cname, nil + return nil, fmt.Errorf("no IP for domain: %v", domain) } return retry(recordCheckAttempts, recordCheckInterval, isReady) @@ -525,7 +543,7 @@ func blockUntilDomainResolves(domain string) (string, error) { func retry[T any](attempts int, sleep time.Duration, f func() (T, error)) (T, error) { var zero T var errOuter error - for range attempts { + for i := 0; i < attempts; i++ { dist, err := f() if err == nil { return dist, nil diff --git a/stackit/internal/services/cdn/testdata/resource-http-base.tf b/stackit/internal/services/cdn/testdata/resource-http-base.tf new file mode 100644 index 000000000..3275fd0a5 --- /dev/null +++ b/stackit/internal/services/cdn/testdata/resource-http-base.tf @@ -0,0 +1,59 @@ +variable "project_id" {} +variable "regions" {} +variable "backend_http_type" {} +variable "backend_origin_url" {} +variable "geofencing_list" {} +variable "blocked_countries" {} +variable "optimizer" {} +variable "origin_request_headers_name" {} +variable "origin_request_headers_value" {} +variable "certificate" {} +variable "private_key" {} + +# dns +variable "dns_zone_name" {} +variable "dns_name" {} +variable "dns_record_name" {} + +resource "stackit_dns_zone" "dns_zone" { + project_id = var.project_id + name = var.dns_zone_name + dns_name = var.dns_name + contact_email = "aa@bb.cc" + type = "primary" + default_ttl = 3600 +} + +resource "stackit_dns_record_set" "dns_record" { + project_id = var.project_id + zone_id = stackit_dns_zone.dns_zone.zone_id + name = var.dns_record_name + type = "CNAME" + records = ["${stackit_cdn_distribution.distribution.domains[0].name}."] +} + +resource "stackit_cdn_distribution" "distribution" { + project_id = var.project_id + config = { + regions = var.regions + optimizer = { + enabled = var.optimizer + } + backend = { + type = var.backend_http_type + origin_url = var.backend_origin_url + origin_request_headers = { + (var.origin_request_headers_name) = var.origin_request_headers_value + } + geofencing = { + (var.backend_origin_url) = var.geofencing_list + } + } + blocked_countries = var.blocked_countries + } +} + +data "stackit_cdn_distribution" "distribution" { + project_id = var.project_id + distribution_id = stackit_cdn_distribution.distribution.distribution_id +} \ No newline at end of file diff --git a/stackit/internal/services/cdn/testdata/resource-http-custom-domain.tf b/stackit/internal/services/cdn/testdata/resource-http-custom-domain.tf new file mode 100644 index 000000000..2caddf9c4 --- /dev/null +++ b/stackit/internal/services/cdn/testdata/resource-http-custom-domain.tf @@ -0,0 +1,17 @@ +# custom domain +resource "stackit_cdn_custom_domain" "custom_domain" { + project_id = var.project_id + distribution_id = stackit_cdn_distribution.distribution.distribution_id + name = "${stackit_dns_record_set.dns_record.name}.${stackit_dns_zone.dns_zone.dns_name}" + certificate = { + certificate = var.certificate + private_key = var.private_key + } +} + +data "stackit_cdn_custom_domain" "custom_domain" { + project_id = var.project_id + distribution_id = stackit_cdn_distribution.distribution.distribution_id + name = "${stackit_dns_record_set.dns_record.name}.${stackit_dns_zone.dns_zone.dns_name}" + depends_on = [stackit_cdn_custom_domain.custom_domain] +} \ No newline at end of file From ef732f9462f6da84ed0a79de0be549930663117a Mon Sep 17 00:00:00 2001 From: Matheus Politano Date: Mon, 30 Mar 2026 15:22:09 +0200 Subject: [PATCH 2/3] chore: delete unsed hcl file --- stackit/internal/services/cdn/cdn_acc_test.go | 3 +- .../services/cdn/testdata/resource-http.tf | 77 ------------------- 2 files changed, 1 insertion(+), 79 deletions(-) delete mode 100644 stackit/internal/services/cdn/testdata/resource-http.tf diff --git a/stackit/internal/services/cdn/cdn_acc_test.go b/stackit/internal/services/cdn/cdn_acc_test.go index d2d9537e3..c673e8eef 100644 --- a/stackit/internal/services/cdn/cdn_acc_test.go +++ b/stackit/internal/services/cdn/cdn_acc_test.go @@ -42,7 +42,6 @@ var ( // Build the full domain name here so we can use it to sign the certificate fullDomainNameHttp = fmt.Sprintf("%s.%s", dnsRecordNameHttp, dnsNameHttp) - // Pass the full domain to the certificate generation cert, key = makeCertAndKey(testutil.OrganizationId, fullDomainNameHttp) ) @@ -114,7 +113,7 @@ func makeCertAndKey(organization string, domain string) (cert, key []byte) { Organization: []string{organization}, CommonName: domain, // Required by most modern TLS validations }, - DNSNames: []string{domain}, // Subject Alternative Name (SAN) is strictly required now + DNSNames: []string{domain}, NotBefore: time.Now(), NotAfter: time.Now().Add(time.Hour), KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, diff --git a/stackit/internal/services/cdn/testdata/resource-http.tf b/stackit/internal/services/cdn/testdata/resource-http.tf deleted file mode 100644 index 489bece48..000000000 --- a/stackit/internal/services/cdn/testdata/resource-http.tf +++ /dev/null @@ -1,77 +0,0 @@ -variable "project_id" {} -variable "regions" {} -variable "backend_http_type" {} -variable "backend_origin_url" {} -variable "geofencing_list" {} -variable "blocked_countries" {} -variable "optimizer" {} -variable "origin_request_headers_name" {} -variable "origin_request_headers_value" {} -variable "certificate" {} -variable "private_key" {} - -# dns -variable "dns_zone_name" {} -variable "dns_name" {} -variable "dns_record_name" {} - -resource "stackit_dns_zone" "dns_zone" { - project_id = var.project_id - name = var.dns_zone_name - dns_name = var.dns_name - contact_email = "aa@bb.cc" - type = "primary" - default_ttl = 3600 -} -resource "stackit_dns_record_set" "dns_record" { - project_id = var.project_id - zone_id = stackit_dns_zone.dns_zone.zone_id - name = var.dns_record_name - type = "CNAME" - records = ["${stackit_cdn_distribution.distribution.domains[0].name}."] -} - -resource "stackit_cdn_distribution" "distribution" { - project_id = var.project_id - config = { - regions = var.regions - optimizer = { - enabled = var.optimizer - } - backend = { - type = var.backend_http_type - origin_url = var.backend_origin_url - origin_request_headers = { - (var.origin_request_headers_name) = var.origin_request_headers_value - } - geofencing = { - (var.backend_origin_url) = var.geofencing_list - } - } - regions = var.regions - blocked_countries = var.blocked_countries - } -} - -data "stackit_cdn_distribution" "distribution" { - project_id = var.project_id - distribution_id = stackit_cdn_distribution.distribution.distribution_id -} - -# custom domain -resource "stackit_cdn_custom_domain" "custom_domain" { - project_id = var.project_id - distribution_id = stackit_cdn_distribution.distribution.distribution_id - name = "${stackit_dns_record_set.dns_record.name}.${stackit_dns_zone.dns_zone.dns_name}" - certificate = { - certificate = var.certificate - private_key = var.private_key - } -} - -data "stackit_cdn_custom_domain" "custom_domain" { - project_id = var.project_id - distribution_id = stackit_cdn_distribution.distribution.distribution_id - name = "${stackit_dns_record_set.dns_record.name}.${stackit_dns_zone.dns_zone.dns_name}" - depends_on = [stackit_cdn_custom_domain.custom_domain] -} From 688fda81b908d4d023c3a4ecb2027d9d59f69fae Mon Sep 17 00:00:00 2001 From: Matheus Politano Date: Tue, 31 Mar 2026 13:24:15 +0200 Subject: [PATCH 3/3] chore: combine param in order to fix lint --- stackit/internal/services/cdn/cdn_acc_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stackit/internal/services/cdn/cdn_acc_test.go b/stackit/internal/services/cdn/cdn_acc_test.go index c673e8eef..42c2387f7 100644 --- a/stackit/internal/services/cdn/cdn_acc_test.go +++ b/stackit/internal/services/cdn/cdn_acc_test.go @@ -101,7 +101,7 @@ func configVarsHttpUpdated() config.Variables { return updatedConfig } -func makeCertAndKey(organization string, domain string) (cert, key []byte) { +func makeCertAndKey(organization, domain string) (cert, key []byte) { privateKey, err := rsa.GenerateKey(cryptoRand.Reader, 2048) if err != nil { fmt.Printf("failed to generate key: %s", err.Error())