diff --git a/docs/data-sources/cdn_distribution.md b/docs/data-sources/cdn_distribution.md
index 799d29c2..a4f28cc7 100644
--- a/docs/data-sources/cdn_distribution.md
+++ b/docs/data-sources/cdn_distribution.md
@@ -46,6 +46,7 @@ data "stackit_cdn_distribution" "example" {
Read-Only:
- `backend` (Attributes) The configured backend for the distribution (see [below for nested schema](#nestedatt--config--backend))
+- `optimizer` (Attributes) Configuration for the Image Optimizer. This is a paid feature that automatically optimizes images to reduce their file size for faster delivery, leading to improved website performance and a better user experience. (see [below for nested schema](#nestedatt--config--optimizer))
- `regions` (List of String) The configured regions where content will be hosted
@@ -58,6 +59,14 @@ Read-Only:
- `type` (String) The configured backend type. Supported values are: `http`.
+
+### Nested Schema for `config.optimizer`
+
+Read-Only:
+
+- `enabled` (Boolean)
+
+
### Nested Schema for `domains`
diff --git a/docs/resources/cdn_distribution.md b/docs/resources/cdn_distribution.md
index 48cfbe9c..5a52fd20 100644
--- a/docs/resources/cdn_distribution.md
+++ b/docs/resources/cdn_distribution.md
@@ -24,6 +24,9 @@ resource "stackit_cdn_distribution" "example_distribution" {
origin_url = "mybackend.onstackit.cloud"
}
regions = ["EU", "US", "ASIA", "AF", "SA"]
+ optimizer = {
+ enabled = true
+ }
}
}
```
@@ -54,6 +57,10 @@ Required:
- `backend` (Attributes) The configured backend for the distribution (see [below for nested schema](#nestedatt--config--backend))
- `regions` (List of String) The configured regions where content will be hosted
+Optional:
+
+- `optimizer` (Attributes) Configuration for the Image Optimizer. This is a paid feature that automatically optimizes images to reduce their file size for faster delivery, leading to improved website performance and a better user experience. (see [below for nested schema](#nestedatt--config--optimizer))
+
### Nested Schema for `config.backend`
@@ -67,6 +74,14 @@ Optional:
- `origin_request_headers` (Map of String) The configured origin request headers for the backend
+
+### Nested Schema for `config.optimizer`
+
+Optional:
+
+- `enabled` (Boolean)
+
+
### Nested Schema for `domains`
diff --git a/examples/resources/stackit_cdn_distribution/resource.tf b/examples/resources/stackit_cdn_distribution/resource.tf
index 66cbca97..39565b22 100644
--- a/examples/resources/stackit_cdn_distribution/resource.tf
+++ b/examples/resources/stackit_cdn_distribution/resource.tf
@@ -6,5 +6,8 @@ resource "stackit_cdn_distribution" "example_distribution" {
origin_url = "mybackend.onstackit.cloud"
}
regions = ["EU", "US", "ASIA", "AF", "SA"]
+ optimizer = {
+ enabled = true
+ }
}
}
diff --git a/stackit/internal/services/cdn/cdn_acc_test.go b/stackit/internal/services/cdn/cdn_acc_test.go
index 6d3b021e..43d24520 100644
--- a/stackit/internal/services/cdn/cdn_acc_test.go
+++ b/stackit/internal/services/cdn/cdn_acc_test.go
@@ -39,6 +39,9 @@ func configResources(regions string) string {
origin_url = "%s"
}
regions = [%s]
+ optimizer = {
+ enabled = true
+ }
}
}
@@ -108,6 +111,7 @@ func TestAccCDNDistributionResource(t *testing.T) {
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.regions.#", "2"),
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.regions.0", "EU"),
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.regions.1", "US"),
+ resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.optimizer.enabled", "true"),
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "project_id", testutil.ProjectId),
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "status", "ACTIVE"),
),
@@ -187,6 +191,7 @@ func TestAccCDNDistributionResource(t *testing.T) {
resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "config.regions.#", "2"),
resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "config.regions.0", "EU"),
resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "config.regions.1", "US"),
+ resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "config.optimizer.enabled", "true"),
resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "project_id", testutil.ProjectId),
resource.TestCheckResourceAttr("data.stackit_cdn_distribution.distribution", "status", "ACTIVE"),
resource.TestCheckResourceAttr("data.stackit_cdn_custom_domain.custom_domain", "status", "ACTIVE"),
@@ -212,6 +217,7 @@ func TestAccCDNDistributionResource(t *testing.T) {
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.regions.0", "EU"),
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.regions.1", "US"),
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.regions.2", "ASIA"),
+ resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "config.optimizer.enabled", "true"),
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "project_id", testutil.ProjectId),
resource.TestCheckResourceAttr("stackit_cdn_distribution.distribution", "status", "ACTIVE"),
resource.TestCheckResourceAttr("stackit_cdn_custom_domain.custom_domain", "status", "ACTIVE"),
diff --git a/stackit/internal/services/cdn/distribution/datasource.go b/stackit/internal/services/cdn/distribution/datasource.go
index d692f3e3..09afc4ff 100644
--- a/stackit/internal/services/cdn/distribution/datasource.go
+++ b/stackit/internal/services/cdn/distribution/datasource.go
@@ -148,7 +148,17 @@ func (r *distributionDataSource) Schema(_ context.Context, _ datasource.SchemaRe
Computed: true,
Description: schemaDescriptions["config_regions"],
ElementType: types.StringType,
- }},
+ },
+ "optimizer": schema.SingleNestedAttribute{
+ Description: schemaDescriptions["config_optimizer"],
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "enabled": schema.BoolAttribute{
+ Computed: true,
+ },
+ },
+ },
+ },
},
},
}
diff --git a/stackit/internal/services/cdn/distribution/resource.go b/stackit/internal/services/cdn/distribution/resource.go
index 22e4af62..d1cc4e33 100644
--- a/stackit/internal/services/cdn/distribution/resource.go
+++ b/stackit/internal/services/cdn/distribution/resource.go
@@ -12,8 +12,10 @@ import (
cdnUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/cdn/utils"
"github.com/google/uuid"
+ "github.com/hashicorp/terraform-plugin-framework-validators/objectvalidator"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/attr"
+ "github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
@@ -52,6 +54,7 @@ var schemaDescriptions = map[string]string{
"config_backend": "The configured backend for the distribution",
"config_regions": "The configured regions where content will be hosted",
"config_backend_type": "The configured backend type. ",
+ "config_optimizer": "Configuration for the Image Optimizer. This is a paid feature that automatically optimizes images to reduce their file size for faster delivery, leading to improved website performance and a better user experience.",
"config_backend_origin_url": "The configured backend type for the distribution",
"config_backend_origin_request_headers": "The configured origin request headers for the backend",
"domain_name": "The name of the domain",
@@ -73,10 +76,13 @@ type Model struct {
}
type distributionConfig struct {
- Backend backend `tfsdk:"backend"` // The backend associated with the distribution
- Regions *[]string `tfsdk:"regions"` // The regions in which data will be cached
+ Backend backend `tfsdk:"backend"` // The backend associated with the distribution
+ Regions *[]string `tfsdk:"regions"` // The regions in which data will be cached
+ Optimizer types.Object `tfsdk:"optimizer"` // The optimizer configuration
+}
+type optimizerConfig struct {
+ Enabled types.Bool `tfsdk:"enabled"`
}
-
type backend struct {
Type string `tfsdk:"type"` // The type of the backend. Currently, only "http" backend is supported
OriginURL string `tfsdk:"origin_url"` // The origin URL of the backend
@@ -86,6 +92,13 @@ type backend struct {
var configTypes = map[string]attr.Type{
"backend": types.ObjectType{AttrTypes: backendTypes},
"regions": types.ListType{ElemType: types.StringType},
+ "optimizer": types.ObjectType{
+ AttrTypes: optimizerTypes,
+ },
+}
+
+var optimizerTypes = map[string]attr.Type{
+ "enabled": types.BoolType,
}
var backendTypes = map[string]attr.Type{
@@ -206,6 +219,20 @@ func (r *distributionResource) Schema(_ context.Context, _ resource.SchemaReques
Required: true,
Description: schemaDescriptions["config"],
Attributes: map[string]schema.Attribute{
+ "optimizer": schema.SingleNestedAttribute{
+ Description: schemaDescriptions["config_optimizer"],
+ Optional: true,
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "enabled": schema.BoolAttribute{
+ Optional: true,
+ Computed: true,
+ },
+ },
+ Validators: []validator.Object{
+ objectvalidator.AlsoRequires(path.MatchRelative().AtName("enabled")),
+ },
+ },
"backend": schema.SingleNestedAttribute{
Required: true,
Description: schemaDescriptions["config_backend"],
@@ -230,7 +257,8 @@ func (r *distributionResource) Schema(_ context.Context, _ resource.SchemaReques
Required: true,
Description: schemaDescriptions["config_regions"],
ElementType: types.StringType,
- }},
+ },
+ },
},
},
}
@@ -349,17 +377,36 @@ func (r *distributionResource) Update(ctx context.Context, req resource.UpdateRe
}
regions = append(regions, *regionEnum)
}
- _, err := r.client.PatchDistribution(ctx, projectId, distributionId).PatchDistributionPayload(cdn.PatchDistributionPayload{
- Config: &cdn.ConfigPatch{
- Backend: &cdn.ConfigPatchBackend{
- HttpBackendPatch: &cdn.HttpBackendPatch{
- OriginRequestHeaders: configModel.Backend.OriginRequestHeaders,
- OriginUrl: &configModel.Backend.OriginURL,
- Type: &configModel.Backend.Type,
- },
+
+ configPatch := &cdn.ConfigPatch{
+ Backend: &cdn.ConfigPatchBackend{
+ HttpBackendPatch: &cdn.HttpBackendPatch{
+ OriginRequestHeaders: configModel.Backend.OriginRequestHeaders,
+ OriginUrl: &configModel.Backend.OriginURL,
+ Type: &configModel.Backend.Type,
},
- Regions: ®ions,
},
+ Regions: ®ions,
+ }
+
+ if !utils.IsUndefined(configModel.Optimizer) {
+ var optimizerModel optimizerConfig
+
+ diags = configModel.Optimizer.As(ctx, &optimizerModel, basetypes.ObjectAsOptions{})
+ if diags.HasError() {
+ core.LogAndAddError(ctx, &resp.Diagnostics, "Update CDN distribution", "Error mapping optimizer config")
+ return
+ }
+
+ optimizer := cdn.NewOptimizerPatch()
+ if !utils.IsUndefined(optimizerModel.Enabled) {
+ optimizer.SetEnabled(optimizerModel.Enabled.ValueBool())
+ }
+ configPatch.Optimizer = optimizer
+ }
+
+ _, err := r.client.PatchDistribution(ctx, projectId, distributionId).PatchDistributionPayload(cdn.PatchDistributionPayload{
+ Config: configPatch,
IntentId: cdn.PtrString(uuid.NewString()),
}).Execute()
if err != nil {
@@ -495,9 +542,24 @@ func mapFields(distribution *cdn.Distribution, model *Model) error {
if diags.HasError() {
return core.DiagsToError(diags)
}
+
+ optimizerVal := types.ObjectNull(optimizerTypes)
+ if o := distribution.Config.Optimizer; o != nil {
+ optimizerEnabled, ok := o.GetEnabledOk()
+ if ok {
+ var diags diag.Diagnostics
+ optimizerVal, diags = types.ObjectValue(optimizerTypes, map[string]attr.Value{
+ "enabled": types.BoolValue(optimizerEnabled),
+ })
+ if diags.HasError() {
+ return core.DiagsToError(diags)
+ }
+ }
+ }
cfg, diags := types.ObjectValue(configTypes, map[string]attr.Value{
- "backend": backend,
- "regions": modelRegions,
+ "backend": backend,
+ "regions": modelRegions,
+ "optimizer": optimizerVal,
})
if diags.HasError() {
return core.DiagsToError(diags)
@@ -553,11 +615,17 @@ func toCreatePayload(ctx context.Context, model *Model) (*cdn.CreateDistribution
if err != nil {
return nil, err
}
+ var optimizer *cdn.Optimizer
+ if cfg.Optimizer != nil {
+ optimizer = cdn.NewOptimizer(cfg.Optimizer.GetEnabled())
+ }
+
payload := &cdn.CreateDistributionPayload{
IntentId: cdn.PtrString(uuid.NewString()),
OriginUrl: cfg.Backend.HttpBackend.OriginUrl,
Regions: cfg.Regions,
OriginRequestHeaders: cfg.Backend.HttpBackend.OriginRequestHeaders,
+ Optimizer: optimizer,
}
return payload, nil
@@ -593,7 +661,8 @@ func convertConfig(ctx context.Context, model *Model) (*cdn.Config, error) {
originRequestHeaders[k] = v
}
}
- return &cdn.Config{
+
+ cdnConfig := &cdn.Config{
Backend: &cdn.ConfigBackend{
HttpBackend: &cdn.HttpBackend{
OriginRequestHeaders: &originRequestHeaders,
@@ -602,5 +671,19 @@ func convertConfig(ctx context.Context, model *Model) (*cdn.Config, error) {
},
},
Regions: ®ions,
- }, nil
+ }
+
+ if !utils.IsUndefined(configModel.Optimizer) {
+ var optimizerModel optimizerConfig
+ diags := configModel.Optimizer.As(ctx, &optimizerModel, basetypes.ObjectAsOptions{})
+ if diags.HasError() {
+ return nil, core.DiagsToError(diags)
+ }
+
+ if !utils.IsUndefined(optimizerModel.Enabled) {
+ cdnConfig.Optimizer = cdn.NewOptimizer(optimizerModel.Enabled.ValueBool())
+ }
+ }
+
+ return cdnConfig, nil
}
diff --git a/stackit/internal/services/cdn/distribution/resource_test.go b/stackit/internal/services/cdn/distribution/resource_test.go
index f3633ef9..970c7535 100644
--- a/stackit/internal/services/cdn/distribution/resource_test.go
+++ b/stackit/internal/services/cdn/distribution/resource_test.go
@@ -24,9 +24,13 @@ func TestToCreatePayload(t *testing.T) {
})
regions := []attr.Value{types.StringValue("EU"), types.StringValue("US")}
regionsFixture := types.ListValueMust(types.StringType, regions)
+ optimizer := types.ObjectValueMust(optimizerTypes, map[string]attr.Value{
+ "enabled": types.BoolValue(true),
+ })
config := types.ObjectValueMust(configTypes, map[string]attr.Value{
- "backend": backend,
- "regions": regionsFixture,
+ "backend": backend,
+ "regions": regionsFixture,
+ "optimizer": types.ObjectNull(optimizerTypes),
})
modelFixture := func(mods ...func(*Model)) *Model {
model := &Model{
@@ -56,6 +60,25 @@ func TestToCreatePayload(t *testing.T) {
},
IsValid: true,
},
+ "happy_path_with_optimizer": {
+ Input: modelFixture(func(m *Model) {
+ m.Config = types.ObjectValueMust(configTypes, map[string]attr.Value{
+ "backend": backend,
+ "regions": regionsFixture,
+ "optimizer": optimizer,
+ })
+ }),
+ Expected: &cdn.CreateDistributionPayload{
+ OriginRequestHeaders: &map[string]string{
+ "testHeader0": "testHeaderValue0",
+ "testHeader1": "testHeaderValue1",
+ },
+ OriginUrl: cdn.PtrString("https://www.mycoolapp.com"),
+ Regions: &[]cdn.Region{"EU", "US"},
+ Optimizer: cdn.NewOptimizer(true),
+ },
+ IsValid: true,
+ },
"sad_path_model_nil": {
Input: nil,
Expected: nil,
@@ -104,9 +127,11 @@ func TestConvertConfig(t *testing.T) {
})
regions := []attr.Value{types.StringValue("EU"), types.StringValue("US")}
regionsFixture := types.ListValueMust(types.StringType, regions)
+ optimizer := types.ObjectValueMust(optimizerTypes, map[string]attr.Value{"enabled": types.BoolValue(true)})
config := types.ObjectValueMust(configTypes, map[string]attr.Value{
- "backend": backend,
- "regions": regionsFixture,
+ "backend": backend,
+ "regions": regionsFixture,
+ "optimizer": types.ObjectNull(optimizerTypes),
})
modelFixture := func(mods ...func(*Model)) *Model {
model := &Model{
@@ -141,6 +166,30 @@ func TestConvertConfig(t *testing.T) {
},
IsValid: true,
},
+ "happy_path_with_optimizer": {
+ Input: modelFixture(func(m *Model) {
+ m.Config = types.ObjectValueMust(configTypes, map[string]attr.Value{
+ "backend": backend,
+ "regions": regionsFixture,
+ "optimizer": optimizer,
+ })
+ }),
+ Expected: &cdn.Config{
+ Backend: &cdn.ConfigBackend{
+ HttpBackend: &cdn.HttpBackend{
+ OriginRequestHeaders: &map[string]string{
+ "testHeader0": "testHeaderValue0",
+ "testHeader1": "testHeaderValue1",
+ },
+ OriginUrl: cdn.PtrString("https://www.mycoolapp.com"),
+ Type: cdn.PtrString("http"),
+ },
+ },
+ Regions: &[]cdn.Region{"EU", "US"},
+ Optimizer: cdn.NewOptimizer(true),
+ },
+ IsValid: true,
+ },
"sad_path_model_nil": {
Input: nil,
Expected: nil,
@@ -188,10 +237,15 @@ func TestMapFields(t *testing.T) {
})
regions := []attr.Value{types.StringValue("EU"), types.StringValue("US")}
regionsFixture := types.ListValueMust(types.StringType, regions)
- config := types.ObjectValueMust(configTypes, map[string]attr.Value{
- "backend": backend,
- "regions": regionsFixture,
+ optimizer := types.ObjectValueMust(optimizerTypes, map[string]attr.Value{
+ "enabled": types.BoolValue(true),
})
+ config := types.ObjectValueMust(configTypes, map[string]attr.Value{
+ "backend": backend,
+ "regions": regionsFixture,
+ "optimizer": types.ObjectNull(optimizerTypes),
+ })
+
emtpyErrorsList := types.ListValueMust(types.StringType, []attr.Value{})
managedDomain := types.ObjectValueMust(domainTypes, map[string]attr.Value{
"name": types.StringValue("test.stackit-cdn.com"),
@@ -219,17 +273,19 @@ func TestMapFields(t *testing.T) {
}
distributionFixture := func(mods ...func(*cdn.Distribution)) *cdn.Distribution {
distribution := &cdn.Distribution{
- Config: &cdn.Config{Backend: &cdn.ConfigBackend{
- HttpBackend: &cdn.HttpBackend{
- OriginRequestHeaders: &map[string]string{
- "testHeader0": "testHeaderValue0",
- "testHeader1": "testHeaderValue1",
+ Config: &cdn.Config{
+ Backend: &cdn.ConfigBackend{
+ HttpBackend: &cdn.HttpBackend{
+ OriginRequestHeaders: &map[string]string{
+ "testHeader0": "testHeaderValue0",
+ "testHeader1": "testHeaderValue1",
+ },
+ OriginUrl: cdn.PtrString("https://www.mycoolapp.com"),
+ Type: cdn.PtrString("http"),
},
- OriginUrl: cdn.PtrString("https://www.mycoolapp.com"),
- Type: cdn.PtrString("http"),
},
- },
- Regions: &[]cdn.Region{"EU", "US"},
+ Regions: &[]cdn.Region{"EU", "US"},
+ Optimizer: nil,
},
CreatedAt: &createdAt,
Domains: &[]cdn.Domain{
@@ -259,6 +315,21 @@ func TestMapFields(t *testing.T) {
Input: distributionFixture(),
IsValid: true,
},
+ "happy_path_with_optimizer": {
+ Expected: expectedModel(func(m *Model) {
+ m.Config = types.ObjectValueMust(configTypes, map[string]attr.Value{
+ "backend": backend,
+ "regions": regionsFixture,
+ "optimizer": optimizer,
+ })
+ }),
+ Input: distributionFixture(func(d *cdn.Distribution) {
+ d.Config.Optimizer = &cdn.Optimizer{
+ Enabled: cdn.PtrBool(true),
+ }
+ }),
+ IsValid: true,
+ },
"happy_path_status_error": {
Expected: expectedModel(func(m *Model) {
m.Status = types.StringValue("ERROR")