Skip to content
Open
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
1 change: 1 addition & 0 deletions docs/data-sources/cdn_custom_domain.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,5 @@ data "stackit_cdn_custom_domain" "example" {

Read-Only:

- `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.
- `version` (Number) A version identifier for the certificate. Required for custom certificates. The certificate will be updated if this field is changed.
6 changes: 4 additions & 2 deletions docs/resources/cdn_custom_domain.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ resource "stackit_cdn_custom_domain" "example" {
distribution_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
name = "https://xxx.xxx"
certificate = {
certificate = "-----BEGIN CERTIFICATE-----\nY2VydGlmaWNhdGVfZGF0YQ==\n-----END CERTIFICATE---"
private_key = "-----BEGIN RSA PRIVATE KEY-----\nY2VydGlmaWNhdGVfZGF0YQ==\n-----END RSA PRIVATE KEY---"
certificate = "-----BEGIN CERTIFICATE-----\nY2VydGlmaWNhdGVfZGF0YQ==\n-----END CERTIFICATE---"
private_key = "-----BEGIN RSA PRIVATE KEY-----\nY2VydGlmaWNhdGVfZGF0YQ==\n-----END RSA PRIVATE KEY---"
skip_dns_check = true
}
}
```
Expand Down Expand Up @@ -53,6 +54,7 @@ Optional:

- `certificate` (String, Sensitive) The PEM-encoded TLS certificate. Required for custom certificates.
- `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.
- `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.

Read-Only:

Expand Down
5 changes: 3 additions & 2 deletions examples/resources/stackit_cdn_custom_domain/resource.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ resource "stackit_cdn_custom_domain" "example" {
distribution_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
name = "https://xxx.xxx"
certificate = {
certificate = "-----BEGIN CERTIFICATE-----\nY2VydGlmaWNhdGVfZGF0YQ==\n-----END CERTIFICATE---"
private_key = "-----BEGIN RSA PRIVATE KEY-----\nY2VydGlmaWNhdGVfZGF0YQ==\n-----END RSA PRIVATE KEY---"
certificate = "-----BEGIN CERTIFICATE-----\nY2VydGlmaWNhdGVfZGF0YQ==\n-----END CERTIFICATE---"
private_key = "-----BEGIN RSA PRIVATE KEY-----\nY2VydGlmaWNhdGVfZGF0YQ==\n-----END RSA PRIVATE KEY---"
skip_dns_check = true
}
}
3 changes: 3 additions & 0 deletions stackit/internal/services/cdn/cdn_acc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,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", fullDomainNameHttp),
resource.TestCheckResourceAttr("stackit_cdn_custom_domain.custom_domain", "certificate.version", "1"),
resource.TestCheckResourceAttr("stackit_cdn_custom_domain.custom_domain", "certificate.skip_dns_check", "false"),
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"),
),
Expand Down Expand Up @@ -396,6 +397,7 @@ func TestAccCDNDistributionHttp(t *testing.T) {
resource.TestCheckResourceAttr("data.stackit_cdn_custom_domain.custom_domain", "status", "ACTIVE"),
resource.TestCheckResourceAttr("data.stackit_cdn_custom_domain.custom_domain", "name", fullDomainNameHttp),
resource.TestCheckResourceAttr("data.stackit_cdn_custom_domain.custom_domain", "certificate.version", "1"),
resource.TestCheckResourceAttr("data.stackit_cdn_custom_domain.custom_domain", "certificate.skip_dns_check", "false"),
resource.TestCheckResourceAttrPair("stackit_cdn_distribution.distribution", "distribution_id", "stackit_cdn_custom_domain.custom_domain", "distribution_id"),
),
},
Expand Down Expand Up @@ -456,6 +458,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", fullDomainNameHttp),
resource.TestCheckResourceAttr("stackit_cdn_custom_domain.custom_domain", "certificate.version", "1"),
resource.TestCheckResourceAttr("stackit_cdn_custom_domain.custom_domain", "certificate.skip_dns_check", "false"),
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"),
),
Expand Down
16 changes: 13 additions & 3 deletions stackit/internal/services/cdn/customdomain/datasource.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ var (
)

var certificateDataSourceTypes = map[string]attr.Type{
"version": types.Int32Type,
"version": types.Int32Type,
"skip_dns_check": types.BoolType,
}

type customDomainDataSource struct {
Expand Down Expand Up @@ -112,6 +113,10 @@ func (r *customDomainDataSource) Schema(_ context.Context, _ datasource.SchemaRe
Description: certificateSchemaDescriptions["version"],
Computed: true,
},
"skip_dns_check": schema.BoolAttribute{
Description: certificateSchemaDescriptions["skip_dns_check"],
Computed: true,
},
},
},
},
Expand Down Expand Up @@ -191,14 +196,19 @@ func mapCustomDomainDataSourceFields(customDomainResponse *cdnSdk.GetCustomDomai
if normalizedCert.Type == "managed" {
model.Certificate = types.ObjectNull(certificateDataSourceTypes)
} else {
// For custom certificates, we only care about the version.
// For custom certificates, we only care about the version and skip_dns_check.
version := types.Int32Null()
if normalizedCert.Version != nil {
version = types.Int32Value(*normalizedCert.Version)
}
skipDnsCheck := types.BoolNull()
if normalizedCert.SkipDnsCheck != nil {
skipDnsCheck = types.BoolValue(*normalizedCert.SkipDnsCheck)
}

certificateObj, diags := types.ObjectValue(certificateDataSourceTypes, map[string]attr.Value{
"version": version,
"version": version,
"skip_dns_check": skipDnsCheck,
})
if diags.HasError() {
return fmt.Errorf("failed to map certificate: %w", core.DiagsToError(diags))
Expand Down
26 changes: 23 additions & 3 deletions stackit/internal/services/cdn/customdomain/datasource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ func TestMapDataSourceFields(t *testing.T) {

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

Expand All @@ -40,8 +41,9 @@ func TestMapDataSourceFields(t *testing.T) {
customVersion := int32(3)
getRespCustom := cdnSdk.GetCustomDomainResponseCertificate{
GetCustomDomainCustomCertificate: &cdnSdk.GetCustomDomainCustomCertificate{
Type: customType,
Version: customVersion,
Type: customType,
Version: customVersion,
SkipDnsCheck: false,
},
}

Expand Down Expand Up @@ -83,6 +85,24 @@ func TestMapDataSourceFields(t *testing.T) {
Input: customDomainFixture(),
IsValid: true,
},
"happy_path_custom_cert_skip_dns_check_true": {
Expected: expectedModel(func(m *customDomainDataSourceModel) {
m.Certificate = types.ObjectValueMust(certificateDataSourceTypes, map[string]attr.Value{
"version": types.Int32Value(3),
"skip_dns_check": types.BoolValue(true),
})
}),
Input: customDomainFixture(func(gcdr *cdnSdk.GetCustomDomainResponse) {
gcdr.Certificate = cdnSdk.GetCustomDomainResponseCertificate{
GetCustomDomainCustomCertificate: &cdnSdk.GetCustomDomainCustomCertificate{
Type: customType,
Version: customVersion,
SkipDnsCheck: true,
},
}
}),
IsValid: true,
},
"happy_path_managed_cert": {
Expected: expectedModel(func(m *customDomainDataSourceModel) {
m.Certificate = types.ObjectNull(certificateDataSourceTypes)
Expand Down
54 changes: 36 additions & 18 deletions stackit/internal/services/cdn/customdomain/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,18 @@ var (
_ resource.ResourceWithImportState = &customDomainResource{}
)
var certificateSchemaDescriptions = map[string]string{
"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.",
"certificate": "The PEM-encoded TLS certificate. Required for custom certificates.",
"private_key": "The PEM-encoded private key for the certificate. Required for custom certificates. The certificate will be updated if this field is changed.",
"version": "A version identifier for the certificate. Required for custom certificates. The certificate will be updated if this field is changed.",
"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.",
"certificate": "The PEM-encoded TLS certificate. Required for custom certificates.",
"private_key": "The PEM-encoded private key for the certificate. Required for custom certificates. The certificate will be updated if this field is changed.",
"version": "A version identifier for the certificate. Required for custom certificates. The certificate will be updated if this field is changed.",
"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.",
}

var certificateTypes = map[string]attr.Type{
"version": types.Int32Type,
"certificate": types.StringType,
"private_key": types.StringType,
"version": types.Int32Type,
"certificate": types.StringType,
"private_key": types.StringType,
"skip_dns_check": types.BoolType,
}

var customDomainSchemaDescriptions = map[string]string{
Expand All @@ -63,9 +65,10 @@ var customDomainSchemaDescriptions = map[string]string{
}

type CertificateModel struct {
Certificate types.String `tfsdk:"certificate"`
PrivateKey types.String `tfsdk:"private_key"`
Version types.Int32 `tfsdk:"version"`
Certificate types.String `tfsdk:"certificate"`
PrivateKey types.String `tfsdk:"private_key"`
Version types.Int32 `tfsdk:"version"`
SkipDnsCheck types.Bool `tfsdk:"skip_dns_check"`
}

type CustomDomainModel struct {
Expand All @@ -87,8 +90,9 @@ func NewCustomDomainResource() resource.Resource {
}

type Certificate struct {
Type string
Version *int32
Type string
Version *int32
SkipDnsCheck *bool
}

func (r *customDomainResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
Expand Down Expand Up @@ -166,6 +170,11 @@ func (r *customDomainResource) Schema(_ context.Context, _ resource.SchemaReques
Description: certificateSchemaDescriptions["version"],
Computed: true,
},
"skip_dns_check": schema.BoolAttribute{
Description: certificateSchemaDescriptions["skip_dns_check"],
Optional: true,
Computed: true,
},
},
},
"status": schema.StringAttribute{
Expand Down Expand Up @@ -417,8 +426,9 @@ func normalizeCertificate(certInput cdnSdk.GetCustomDomainResponseCertificate) (
// Now we process the extracted certificates
if customCert != nil && customCert.Type != "" {
return Certificate{
Type: customCert.Type,
Version: new(customCert.Version),
Type: customCert.Type,
Version: &customCert.Version,
SkipDnsCheck: &customCert.SkipDnsCheck,
}, nil
}

Expand Down Expand Up @@ -465,6 +475,7 @@ func toCertificatePayload(ctx context.Context, model *CustomDomainModel) (*cdnSd
keyStr,
"custom",
)
customCert.SkipDnsCheck = conversion.BoolValueToPointer(certModel.SkipDnsCheck)
certPayload := cdnSdk.PutCustomDomainCustomCertificateAsPutCustomDomainPayloadCertificate(customCert)

return &certPayload, nil
Expand Down Expand Up @@ -495,11 +506,12 @@ func mapCustomDomainResourceFields(customDomainResponse *cdnSdk.GetCustomDomainR
model.Certificate = types.ObjectNull(certificateTypes)
} else {
// If the certificate is custom, we need to preserve the user-configured
// certificate and private key from the plan/state, and only update the computed version.
// certificate and private key from the plan/state, and update the computed version and skip_dns_check.
certAttributes := map[string]attr.Value{
"certificate": types.StringNull(), // Default to null
"private_key": types.StringNull(), // Default to null
"version": types.Int32Null(),
"certificate": types.StringNull(), // Default to null
"private_key": types.StringNull(), // Default to null
"version": types.Int32Null(),
"skip_dns_check": types.BoolNull(),
}

// Get existing values from the model's certificate object if it exists
Expand All @@ -511,12 +523,18 @@ func mapCustomDomainResourceFields(customDomainResponse *cdnSdk.GetCustomDomainR
if val, ok := existingAttrs["private_key"]; ok {
certAttributes["private_key"] = val
}
if val, ok := existingAttrs["skip_dns_check"]; ok {
certAttributes["skip_dns_check"] = val
}
}

// Set the computed version from the API response
if normalizedCert.Version != nil {
certAttributes["version"] = types.Int32Value(*normalizedCert.Version)
}
if normalizedCert.SkipDnsCheck != nil {
certAttributes["skip_dns_check"] = types.BoolValue(*normalizedCert.SkipDnsCheck)
}

certificateObj, diags := types.ObjectValue(certificateTypes, certAttributes)
if diags.HasError() {
Expand Down
Loading
Loading