diff --git a/docs/resources/service_compute.md b/docs/resources/service_compute.md
index 8fa672e3b..22da0d3dc 100644
--- a/docs/resources/service_compute.md
+++ b/docs/resources/service_compute.md
@@ -661,12 +661,9 @@ Optional:
Optional:
- `fanout` (Boolean) Enable Fanout support
+- `name` (String) Used by the provider to identify modified settings (changing this value will force the entire block to be deleted, then recreated)
- `websockets` (Boolean) Enable WebSockets support
-Read-Only:
-
-- `name` (String) Used internally by the provider to identify modified settings
-
### Nested Schema for `resource_link`
diff --git a/docs/resources/service_vcl.md b/docs/resources/service_vcl.md
index 5ec8970c6..2069a7e3b 100644
--- a/docs/resources/service_vcl.md
+++ b/docs/resources/service_vcl.md
@@ -1093,13 +1093,10 @@ Optional:
- `brotli_compression` (Boolean) Enable Brotli Compression support
- `domain_inspector` (Boolean) Enable Domain Inspector support
- `image_optimizer` (Boolean) Enable Image Optimizer support (requires at least one backend with a `shield` attribute)
+- `name` (String) Used by the provider to identify modified settings (changing this value will force the entire block to be deleted, then recreated)
- `origin_inspector` (Boolean) Enable Origin Inspector support
- `websockets` (Boolean) Enable WebSockets support
-Read-Only:
-
-- `name` (String) Used internally by the provider to identify modified settings
-
### Nested Schema for `rate_limiter`
diff --git a/fastly/block_fastly_service_product_enablement.go b/fastly/block_fastly_service_product_enablement.go
index 6555a030c..fa40c3b6f 100644
--- a/fastly/block_fastly_service_product_enablement.go
+++ b/fastly/block_fastly_service_product_enablement.go
@@ -36,8 +36,9 @@ func (h *ProductEnablementServiceAttributeHandler) GetSchema() *schema.Schema {
blockAttributes := map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
- Computed: true,
- Description: "Used internally by the provider to identify modified settings",
+ Optional: true,
+ Default: "products",
+ Description: "Used by the provider to identify modified settings (changing this value will force the entire block to be deleted, then recreated)",
},
}
@@ -45,7 +46,6 @@ func (h *ProductEnablementServiceAttributeHandler) GetSchema() *schema.Schema {
blockAttributes["fanout"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
- Default: false,
Description: "Enable Fanout support",
}
}
@@ -54,25 +54,21 @@ func (h *ProductEnablementServiceAttributeHandler) GetSchema() *schema.Schema {
blockAttributes["brotli_compression"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
- Default: false,
Description: "Enable Brotli Compression support",
}
blockAttributes["domain_inspector"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
- Default: false,
Description: "Enable Domain Inspector support",
}
blockAttributes["image_optimizer"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
- Default: false,
Description: "Enable Image Optimizer support (requires at least one backend with a `shield` attribute)",
}
blockAttributes["origin_inspector"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
- Default: false,
Description: "Enable Origin Inspector support",
}
}
@@ -81,7 +77,6 @@ func (h *ProductEnablementServiceAttributeHandler) GetSchema() *schema.Schema {
blockAttributes["websockets"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
- Default: false,
Description: "Enable WebSockets support",
}
@@ -214,11 +209,6 @@ func (h *ProductEnablementServiceAttributeHandler) Read(_ context.Context, d *sc
// The API returns a 400 if a product is not enabled.
// The API client returns an error if a non-2xx is returned from the API.
- var (
- enabled bool
- err error
- )
-
// NOTE: We must set name to a unique value for a plan diff to be calculated.
//
// This value can be static because (like with the 'package' block) there can
@@ -229,72 +219,54 @@ func (h *ProductEnablementServiceAttributeHandler) Read(_ context.Context, d *sc
// This is done so we can benefit from the 'modified' map data passed to the
// 'update' CRUD method.
result := map[string]any{
- "name": "products",
+ "name": localState[0].(map[string]any)["name"].(string),
}
if h.GetServiceMetadata().serviceType == ServiceTypeCompute {
- enabled = false
- _, err = conn.GetProduct(&gofastly.ProductEnablementInput{
+ if _, err := conn.GetProduct(&gofastly.ProductEnablementInput{
ProductID: gofastly.ProductFanout,
ServiceID: d.Id(),
- })
- if err == nil {
- enabled = true
+ }); err == nil {
+ result["fanout"] = true
}
- result["fanout"] = enabled
}
if h.GetServiceMetadata().serviceType == ServiceTypeVCL {
- enabled = false
- _, err = conn.GetProduct(&gofastly.ProductEnablementInput{
+ if _, err := conn.GetProduct(&gofastly.ProductEnablementInput{
ProductID: gofastly.ProductBrotliCompression,
ServiceID: d.Id(),
- })
- if err == nil {
- enabled = true
+ }); err == nil {
+ result["brotli_compression"] = true
}
- result["brotli_compression"] = enabled
- enabled = false
- _, err = conn.GetProduct(&gofastly.ProductEnablementInput{
+ if _, err := conn.GetProduct(&gofastly.ProductEnablementInput{
ProductID: gofastly.ProductDomainInspector,
ServiceID: d.Id(),
- })
- if err == nil {
- enabled = true
+ }); err == nil {
+ result["domain_inspector"] = true
}
- result["domain_inspector"] = enabled
- enabled = false
- _, err = conn.GetProduct(&gofastly.ProductEnablementInput{
+ if _, err := conn.GetProduct(&gofastly.ProductEnablementInput{
ProductID: gofastly.ProductImageOptimizer,
ServiceID: d.Id(),
- })
- if err == nil {
- enabled = true
+ }); err == nil {
+ result["image_optimizer"] = true
}
- result["image_optimizer"] = enabled
- enabled = false
- _, err = conn.GetProduct(&gofastly.ProductEnablementInput{
+ if _, err := conn.GetProduct(&gofastly.ProductEnablementInput{
ProductID: gofastly.ProductOriginInspector,
ServiceID: d.Id(),
- })
- if err == nil {
- enabled = true
+ }); err == nil {
+ result["origin_inspector"] = true
}
- result["origin_inspector"] = enabled
}
- enabled = false
- _, err = conn.GetProduct(&gofastly.ProductEnablementInput{
+ if _, err := conn.GetProduct(&gofastly.ProductEnablementInput{
ProductID: gofastly.ProductWebSockets,
ServiceID: d.Id(),
- })
- if err == nil {
- enabled = true
+ }); err == nil {
+ result["websockets"] = true
}
- result["websockets"] = enabled
results := []map[string]any{result}
@@ -309,32 +281,19 @@ func (h *ProductEnablementServiceAttributeHandler) Read(_ context.Context, d *sc
// Update updates the resource.
//
-// IMPORTANT: The Update method is never called due to an implementation bug.
-//
-// It's a side-effect of the SetDiff logic https://github.com/fastly/terraform-provider-fastly/blob/6e03cce3127a30db94c1cdfa8eb621d9a7231989/fastly/service_crud_attribute_definition.go#L89-L95
-// The SetDiff logic uses `name` as a computed key (i.e. if there’s a change to
-// `name`, then it means the resource has changed and needs to be recreated).
+// IMPORTANT: We ignore errors related to entitlement when updating.
//
-// The problem is we defined `name` as a Computed attribute, which means it will
-// always be marked as being changed as it's reset to an empty string due to it
-// being a Computed attribute where the value is known only AFTER an apply.
-//
-// Fixing this bug would mean needing to change `name` from being Computed to
-// Optional and also setting a default to match the hardcoded value "products"
-// that we used when it was Computed, and to configure the attribute to be
-// ignored by the Terraform diff processing logic. That should in theory make it
-// a non-breaking change.
-//
-// This isn't the end of the world, it just means there are a few more API calls
-// made. We're also (as of Sept 2023) in the process of rewriting the Terraform
-// provider and so it might be best to resolve this as part of the rewrite.
+// This is to provide a non-breaking workaround for customers who used an older
+// version of the Fastly Terraform provider. See details in the PR:
+// https://github.com/fastly/terraform-provider-fastly/pull/763
func (h *ProductEnablementServiceAttributeHandler) Update(_ context.Context, d *schema.ResourceData, _, modified map[string]any, serviceVersion int, conn *gofastly.Client) error {
serviceID := d.Id()
+ log.Println("[DEBUG] Update Product Enablement")
if h.GetServiceMetadata().serviceType == ServiceTypeCompute {
if v, ok := modified["fanout"]; ok {
if v.(bool) {
- log.Println("[DEBUG] fanout set")
+ log.Println("[DEBUG] fanout will be enabled")
_, err := conn.EnableProduct(&gofastly.ProductEnablementInput{
ProductID: gofastly.ProductFanout,
ServiceID: serviceID,
@@ -343,13 +302,15 @@ func (h *ProductEnablementServiceAttributeHandler) Update(_ context.Context, d *
return fmt.Errorf("failed to enable fanout: %w", err)
}
} else {
- log.Println("[DEBUG] fanout not set")
+ log.Println("[DEBUG] fanout will be disabled")
err := conn.DisableProduct(&gofastly.ProductEnablementInput{
ProductID: gofastly.ProductFanout,
ServiceID: serviceID,
})
if err != nil {
- return fmt.Errorf("failed to disable fanout: %w", err)
+ if e := h.checkAPIError(err); e != nil {
+ return e
+ }
}
}
}
@@ -358,7 +319,7 @@ func (h *ProductEnablementServiceAttributeHandler) Update(_ context.Context, d *
if h.GetServiceMetadata().serviceType == ServiceTypeVCL {
if v, ok := modified["brotli_compression"]; ok {
if v.(bool) {
- log.Println("[DEBUG] brotli_compression set")
+ log.Println("[DEBUG] brotli_compression will be enabled")
_, err := conn.EnableProduct(&gofastly.ProductEnablementInput{
ProductID: gofastly.ProductBrotliCompression,
ServiceID: serviceID,
@@ -367,20 +328,22 @@ func (h *ProductEnablementServiceAttributeHandler) Update(_ context.Context, d *
return fmt.Errorf("failed to enable brotli_compression: %w", err)
}
} else {
- log.Println("[DEBUG] brotli_compression not set")
+ log.Println("[DEBUG] brotli_compression will be disabled")
err := conn.DisableProduct(&gofastly.ProductEnablementInput{
ProductID: gofastly.ProductBrotliCompression,
ServiceID: serviceID,
})
if err != nil {
- return fmt.Errorf("failed to disable brotli_compression: %w", err)
+ if e := h.checkAPIError(err); e != nil {
+ return e
+ }
}
}
}
if v, ok := modified["domain_inspector"]; ok {
if v.(bool) {
- log.Println("[DEBUG] domain_inspector set")
+ log.Println("[DEBUG] domain_inspector will be enabled")
_, err := conn.EnableProduct(&gofastly.ProductEnablementInput{
ProductID: gofastly.ProductDomainInspector,
ServiceID: serviceID,
@@ -389,20 +352,22 @@ func (h *ProductEnablementServiceAttributeHandler) Update(_ context.Context, d *
return fmt.Errorf("failed to enable domain_inspector: %w", err)
}
} else {
- log.Println("[DEBUG] domain_inspector not set")
+ log.Println("[DEBUG] domain_inspector will be disabled")
err := conn.DisableProduct(&gofastly.ProductEnablementInput{
ProductID: gofastly.ProductDomainInspector,
ServiceID: serviceID,
})
if err != nil {
- return fmt.Errorf("failed to disable domain_inspector: %w", err)
+ if e := h.checkAPIError(err); e != nil {
+ return e
+ }
}
}
}
if v, ok := modified["image_optimizer"]; ok {
if v.(bool) {
- log.Println("[DEBUG] image_optimizer set")
+ log.Println("[DEBUG] image_optimizer will be enabled")
_, err := conn.EnableProduct(&gofastly.ProductEnablementInput{
ProductID: gofastly.ProductImageOptimizer,
ServiceID: serviceID,
@@ -411,20 +376,22 @@ func (h *ProductEnablementServiceAttributeHandler) Update(_ context.Context, d *
return fmt.Errorf("failed to enable image_optimizer: %w", err)
}
} else {
- log.Println("[DEBUG] image_optimizer not set")
+ log.Println("[DEBUG] image_optimizer will be disabled")
err := conn.DisableProduct(&gofastly.ProductEnablementInput{
ProductID: gofastly.ProductImageOptimizer,
ServiceID: serviceID,
})
if err != nil {
- return fmt.Errorf("failed to disable image_optimizer: %w", err)
+ if e := h.checkAPIError(err); e != nil {
+ return e
+ }
}
}
}
if v, ok := modified["origin_inspector"]; ok {
if v.(bool) {
- log.Println("[DEBUG] origin_inspector set")
+ log.Println("[DEBUG] origin_inspector will be enabled")
_, err := conn.EnableProduct(&gofastly.ProductEnablementInput{
ProductID: gofastly.ProductOriginInspector,
ServiceID: serviceID,
@@ -433,13 +400,15 @@ func (h *ProductEnablementServiceAttributeHandler) Update(_ context.Context, d *
return fmt.Errorf("failed to enable origin_inspector: %w", err)
}
} else {
- log.Println("[DEBUG] origin_inspector not set")
+ log.Println("[DEBUG] origin_inspector will be disabled")
err := conn.DisableProduct(&gofastly.ProductEnablementInput{
ProductID: gofastly.ProductOriginInspector,
ServiceID: serviceID,
})
if err != nil {
- return fmt.Errorf("failed to disable origin_inspector: %w", err)
+ if e := h.checkAPIError(err); e != nil {
+ return e
+ }
}
}
}
@@ -447,7 +416,7 @@ func (h *ProductEnablementServiceAttributeHandler) Update(_ context.Context, d *
if v, ok := modified["websockets"]; ok {
if v.(bool) {
- log.Println("[DEBUG] websockets set")
+ log.Println("[DEBUG] websockets will be enabled")
_, err := conn.EnableProduct(&gofastly.ProductEnablementInput{
ProductID: gofastly.ProductWebSockets,
ServiceID: serviceID,
@@ -456,13 +425,15 @@ func (h *ProductEnablementServiceAttributeHandler) Update(_ context.Context, d *
return fmt.Errorf("failed to enable websockets: %w", err)
}
} else {
- log.Println("[DEBUG] websockets not set")
+ log.Println("[DEBUG] websockets will be disabled")
err := conn.DisableProduct(&gofastly.ProductEnablementInput{
ProductID: gofastly.ProductWebSockets,
ServiceID: serviceID,
})
if err != nil {
- return fmt.Errorf("failed to disable websockets: %w", err)
+ if e := h.checkAPIError(err); e != nil {
+ return e
+ }
}
}
}
diff --git a/tests/interface/main.tf b/tests/interface/main.tf
index 2e5e6cbe9..fc8c66583 100644
--- a/tests/interface/main.tf
+++ b/tests/interface/main.tf
@@ -137,11 +137,7 @@ resource "fastly_service_vcl" "interface-test-project" {
}
product_enablement {
- brotli_compression = false
- domain_inspector = false
- image_optimizer = false
- origin_inspector = false
- websockets = false
+ brotli_compression = true
}
rate_limiter {