Skip to content

Commit 0c8ee62

Browse files
authored
feat(cdn): Add skip_dns_check property field (#1727)
Adds skip_dns_check attribute to cdn customdomain resource and datasource
1 parent fc2a33c commit 0c8ee62

9 files changed

Lines changed: 209 additions & 53 deletions

File tree

docs/data-sources/cdn_custom_domain.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,5 @@ data "stackit_cdn_custom_domain" "example" {
4747

4848
Read-Only:
4949

50+
- `skip_dns_check` (Boolean) When true, skips the verification check that the custom domain points to the distribution domain via CNAME. Useful for zero-downtime migrations.
5051
- `version` (Number) A version identifier for the certificate. Required for custom certificates. The certificate will be updated if this field is changed.

docs/resources/cdn_custom_domain.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ resource "stackit_cdn_custom_domain" "example" {
2121
distribution_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
2222
name = "https://xxx.xxx"
2323
certificate = {
24-
certificate = "-----BEGIN CERTIFICATE-----\nY2VydGlmaWNhdGVfZGF0YQ==\n-----END CERTIFICATE---"
25-
private_key = "-----BEGIN RSA PRIVATE KEY-----\nY2VydGlmaWNhdGVfZGF0YQ==\n-----END RSA PRIVATE KEY---"
24+
certificate = "-----BEGIN CERTIFICATE-----\nY2VydGlmaWNhdGVfZGF0YQ==\n-----END CERTIFICATE---"
25+
private_key = "-----BEGIN RSA PRIVATE KEY-----\nY2VydGlmaWNhdGVfZGF0YQ==\n-----END RSA PRIVATE KEY---"
26+
skip_dns_check = true
2627
}
2728
}
2829
```
@@ -53,6 +54,7 @@ Optional:
5354

5455
- `certificate` (String, Sensitive) The PEM-encoded TLS certificate. Required for custom certificates.
5556
- `private_key` (String, Sensitive) The PEM-encoded private key for the certificate. Required for custom certificates. The certificate will be updated if this field is changed.
57+
- `skip_dns_check` (Boolean) When true, skips the verification check that the custom domain points to the distribution domain via CNAME. Useful for zero-downtime migrations.
5658

5759
Read-Only:
5860

examples/resources/stackit_cdn_custom_domain/resource.tf

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ resource "stackit_cdn_custom_domain" "example" {
33
distribution_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
44
name = "https://xxx.xxx"
55
certificate = {
6-
certificate = "-----BEGIN CERTIFICATE-----\nY2VydGlmaWNhdGVfZGF0YQ==\n-----END CERTIFICATE---"
7-
private_key = "-----BEGIN RSA PRIVATE KEY-----\nY2VydGlmaWNhdGVfZGF0YQ==\n-----END RSA PRIVATE KEY---"
6+
certificate = "-----BEGIN CERTIFICATE-----\nY2VydGlmaWNhdGVfZGF0YQ==\n-----END CERTIFICATE---"
7+
private_key = "-----BEGIN RSA PRIVATE KEY-----\nY2VydGlmaWNhdGVfZGF0YQ==\n-----END RSA PRIVATE KEY---"
8+
skip_dns_check = true
89
}
910
}

stackit/internal/services/cdn/cdn_acc_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ func TestAccCDNDistributionHttp(t *testing.T) {
287287
resource.TestCheckResourceAttr("stackit_cdn_custom_domain.custom_domain", "status", "ACTIVE"),
288288
resource.TestCheckResourceAttr("stackit_cdn_custom_domain.custom_domain", "name", fullDomainNameHttp),
289289
resource.TestCheckResourceAttr("stackit_cdn_custom_domain.custom_domain", "certificate.version", "1"),
290+
resource.TestCheckResourceAttr("stackit_cdn_custom_domain.custom_domain", "certificate.skip_dns_check", "true"),
290291
resource.TestCheckResourceAttrPair("stackit_cdn_distribution.distribution", "distribution_id", "stackit_cdn_custom_domain.custom_domain", "distribution_id"),
291292
resource.TestCheckResourceAttrPair("stackit_cdn_distribution.distribution", "project_id", "stackit_cdn_custom_domain.custom_domain", "project_id"),
292293
),
@@ -396,6 +397,7 @@ func TestAccCDNDistributionHttp(t *testing.T) {
396397
resource.TestCheckResourceAttr("data.stackit_cdn_custom_domain.custom_domain", "status", "ACTIVE"),
397398
resource.TestCheckResourceAttr("data.stackit_cdn_custom_domain.custom_domain", "name", fullDomainNameHttp),
398399
resource.TestCheckResourceAttr("data.stackit_cdn_custom_domain.custom_domain", "certificate.version", "1"),
400+
resource.TestCheckResourceAttr("data.stackit_cdn_custom_domain.custom_domain", "certificate.skip_dns_check", "true"),
399401
resource.TestCheckResourceAttrPair("stackit_cdn_distribution.distribution", "distribution_id", "stackit_cdn_custom_domain.custom_domain", "distribution_id"),
400402
),
401403
},
@@ -456,6 +458,7 @@ func TestAccCDNDistributionHttp(t *testing.T) {
456458
resource.TestCheckResourceAttr("stackit_cdn_custom_domain.custom_domain", "status", "ACTIVE"),
457459
resource.TestCheckResourceAttr("stackit_cdn_custom_domain.custom_domain", "name", fullDomainNameHttp),
458460
resource.TestCheckResourceAttr("stackit_cdn_custom_domain.custom_domain", "certificate.version", "1"),
461+
resource.TestCheckResourceAttr("stackit_cdn_custom_domain.custom_domain", "certificate.skip_dns_check", "true"),
459462
resource.TestCheckResourceAttrPair("stackit_cdn_distribution.distribution", "distribution_id", "stackit_cdn_custom_domain.custom_domain", "distribution_id"),
460463
resource.TestCheckResourceAttrPair("stackit_cdn_distribution.distribution", "project_id", "stackit_cdn_custom_domain.custom_domain", "project_id"),
461464
),

stackit/internal/services/cdn/customdomain/datasource.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ var (
2929
)
3030

3131
var certificateDataSourceTypes = map[string]attr.Type{
32-
"version": types.Int32Type,
32+
"version": types.Int32Type,
33+
"skip_dns_check": types.BoolType,
3334
}
3435

3536
type customDomainDataSource struct {
@@ -112,6 +113,10 @@ func (r *customDomainDataSource) Schema(_ context.Context, _ datasource.SchemaRe
112113
Description: certificateSchemaDescriptions["version"],
113114
Computed: true,
114115
},
116+
"skip_dns_check": schema.BoolAttribute{
117+
Description: certificateSchemaDescriptions["skip_dns_check"],
118+
Computed: true,
119+
},
115120
},
116121
},
117122
},
@@ -191,14 +196,16 @@ func mapCustomDomainDataSourceFields(customDomainResponse *cdnSdk.GetCustomDomai
191196
if normalizedCert.Type == "managed" {
192197
model.Certificate = types.ObjectNull(certificateDataSourceTypes)
193198
} else {
194-
// For custom certificates, we only care about the version.
199+
// For custom certificates, we only care about the version and skip_dns_check.
195200
version := types.Int32Null()
196201
if normalizedCert.Version != nil {
197202
version = types.Int32Value(*normalizedCert.Version)
198203
}
204+
skipDnsCheck := types.BoolPointerValue(normalizedCert.SkipDnsCheck)
199205

200206
certificateObj, diags := types.ObjectValue(certificateDataSourceTypes, map[string]attr.Value{
201-
"version": version,
207+
"version": version,
208+
"skip_dns_check": skipDnsCheck,
202209
})
203210
if diags.HasError() {
204211
return fmt.Errorf("failed to map certificate: %w", core.DiagsToError(diags))

stackit/internal/services/cdn/customdomain/datasource_test.go

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ func TestMapDataSourceFields(t *testing.T) {
1414

1515
// Expected certificate object when a custom certificate is returned
1616
certAttributes := map[string]attr.Value{
17-
"version": types.Int32Value(3),
17+
"version": types.Int32Value(3),
18+
"skip_dns_check": types.BoolValue(false),
1819
}
1920
certificateObj, _ := types.ObjectValue(certificateDataSourceTypes, certAttributes)
2021

@@ -40,8 +41,9 @@ func TestMapDataSourceFields(t *testing.T) {
4041
customVersion := int32(3)
4142
getRespCustom := cdnSdk.GetCustomDomainResponseCertificate{
4243
GetCustomDomainCustomCertificate: &cdnSdk.GetCustomDomainCustomCertificate{
43-
Type: customType,
44-
Version: customVersion,
44+
Type: customType,
45+
Version: customVersion,
46+
SkipDnsCheck: false,
4547
},
4648
}
4749

@@ -83,6 +85,24 @@ func TestMapDataSourceFields(t *testing.T) {
8385
Input: customDomainFixture(),
8486
IsValid: true,
8587
},
88+
"happy_path_custom_cert_skip_dns_check_true": {
89+
Expected: expectedModel(func(m *customDomainDataSourceModel) {
90+
m.Certificate = types.ObjectValueMust(certificateDataSourceTypes, map[string]attr.Value{
91+
"version": types.Int32Value(3),
92+
"skip_dns_check": types.BoolValue(true),
93+
})
94+
}),
95+
Input: customDomainFixture(func(gcdr *cdnSdk.GetCustomDomainResponse) {
96+
gcdr.Certificate = cdnSdk.GetCustomDomainResponseCertificate{
97+
GetCustomDomainCustomCertificate: &cdnSdk.GetCustomDomainCustomCertificate{
98+
Type: customType,
99+
Version: customVersion,
100+
SkipDnsCheck: true,
101+
},
102+
}
103+
}),
104+
IsValid: true,
105+
},
86106
"happy_path_managed_cert": {
87107
Expected: expectedModel(func(m *customDomainDataSourceModel) {
88108
m.Certificate = types.ObjectNull(certificateDataSourceTypes)

stackit/internal/services/cdn/customdomain/resource.go

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/hashicorp/terraform-plugin-framework/attr"
2121
"github.com/hashicorp/terraform-plugin-framework/resource"
2222
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
23+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
2324
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
2425
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
2526
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
@@ -42,16 +43,18 @@ var (
4243
_ resource.ResourceWithImportState = &customDomainResource{}
4344
)
4445
var certificateSchemaDescriptions = map[string]string{
45-
"main": "The TLS certificate for the custom domain. If omitted, a managed certificate will be used. If the block is specified, a custom certificate is used.",
46-
"certificate": "The PEM-encoded TLS certificate. Required for custom certificates.",
47-
"private_key": "The PEM-encoded private key for the certificate. Required for custom certificates. The certificate will be updated if this field is changed.",
48-
"version": "A version identifier for the certificate. Required for custom certificates. The certificate will be updated if this field is changed.",
46+
"main": "The TLS certificate for the custom domain. If omitted, a managed certificate will be used. If the block is specified, a custom certificate is used.",
47+
"certificate": "The PEM-encoded TLS certificate. Required for custom certificates.",
48+
"private_key": "The PEM-encoded private key for the certificate. Required for custom certificates. The certificate will be updated if this field is changed.",
49+
"version": "A version identifier for the certificate. Required for custom certificates. The certificate will be updated if this field is changed.",
50+
"skip_dns_check": "When true, skips the verification check that the custom domain points to the distribution domain via CNAME. Useful for zero-downtime migrations.",
4951
}
5052

5153
var certificateTypes = map[string]attr.Type{
52-
"version": types.Int32Type,
53-
"certificate": types.StringType,
54-
"private_key": types.StringType,
54+
"version": types.Int32Type,
55+
"certificate": types.StringType,
56+
"private_key": types.StringType,
57+
"skip_dns_check": types.BoolType,
5558
}
5659

5760
var customDomainSchemaDescriptions = map[string]string{
@@ -63,9 +66,10 @@ var customDomainSchemaDescriptions = map[string]string{
6366
}
6467

6568
type CertificateModel struct {
66-
Certificate types.String `tfsdk:"certificate"`
67-
PrivateKey types.String `tfsdk:"private_key"`
68-
Version types.Int32 `tfsdk:"version"`
69+
Certificate types.String `tfsdk:"certificate"`
70+
PrivateKey types.String `tfsdk:"private_key"`
71+
Version types.Int32 `tfsdk:"version"`
72+
SkipDnsCheck types.Bool `tfsdk:"skip_dns_check"`
6973
}
7074

7175
type CustomDomainModel struct {
@@ -87,8 +91,9 @@ func NewCustomDomainResource() resource.Resource {
8791
}
8892

8993
type Certificate struct {
90-
Type string
91-
Version *int32
94+
Type string
95+
Version *int32
96+
SkipDnsCheck *bool
9297
}
9398

9499
func (r *customDomainResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
@@ -166,6 +171,12 @@ func (r *customDomainResource) Schema(_ context.Context, _ resource.SchemaReques
166171
Description: certificateSchemaDescriptions["version"],
167172
Computed: true,
168173
},
174+
"skip_dns_check": schema.BoolAttribute{
175+
Description: certificateSchemaDescriptions["skip_dns_check"],
176+
Optional: true,
177+
Computed: true,
178+
Default: booldefault.StaticBool(false),
179+
},
169180
},
170181
},
171182
"status": schema.StringAttribute{
@@ -417,8 +428,9 @@ func normalizeCertificate(certInput cdnSdk.GetCustomDomainResponseCertificate) (
417428
// Now we process the extracted certificates
418429
if customCert != nil && customCert.Type != "" {
419430
return Certificate{
420-
Type: customCert.Type,
421-
Version: new(customCert.Version),
431+
Type: customCert.Type,
432+
Version: &customCert.Version,
433+
SkipDnsCheck: &customCert.SkipDnsCheck,
422434
}, nil
423435
}
424436

@@ -465,6 +477,7 @@ func toCertificatePayload(ctx context.Context, model *CustomDomainModel) (*cdnSd
465477
keyStr,
466478
"custom",
467479
)
480+
customCert.SkipDnsCheck = conversion.BoolValueToPointer(certModel.SkipDnsCheck)
468481
certPayload := cdnSdk.PutCustomDomainCustomCertificateAsPutCustomDomainPayloadCertificate(customCert)
469482

470483
return &certPayload, nil
@@ -495,11 +508,12 @@ func mapCustomDomainResourceFields(customDomainResponse *cdnSdk.GetCustomDomainR
495508
model.Certificate = types.ObjectNull(certificateTypes)
496509
} else {
497510
// If the certificate is custom, we need to preserve the user-configured
498-
// certificate and private key from the plan/state, and only update the computed version.
511+
// certificate and private key from the plan/state, and update the computed version and skip_dns_check.
499512
certAttributes := map[string]attr.Value{
500-
"certificate": types.StringNull(), // Default to null
501-
"private_key": types.StringNull(), // Default to null
502-
"version": types.Int32Null(),
513+
"certificate": types.StringNull(), // Default to null
514+
"private_key": types.StringNull(), // Default to null
515+
"version": types.Int32Null(),
516+
"skip_dns_check": types.BoolNull(),
503517
}
504518

505519
// Get existing values from the model's certificate object if it exists
@@ -517,6 +531,7 @@ func mapCustomDomainResourceFields(customDomainResponse *cdnSdk.GetCustomDomainR
517531
if normalizedCert.Version != nil {
518532
certAttributes["version"] = types.Int32Value(*normalizedCert.Version)
519533
}
534+
certAttributes["skip_dns_check"] = types.BoolPointerValue(normalizedCert.SkipDnsCheck)
520535

521536
certificateObj, diags := types.ObjectValue(certificateTypes, certAttributes)
522537
if diags.HasError() {

0 commit comments

Comments
 (0)