Skip to content

Commit

Permalink
PATCH endpoint for TLS subscriptions (#370)
Browse files Browse the repository at this point in the history
* Add update function to TLS Subscriptions resource

Unable to test as it requires a subscription to be in the "issued" state
which would depend on another provider to handle DNS.

Takes advantage of Terraform's CustomizeDiff to decide whether to update
the resource in place or recreate it.

* Test updating subscription

Unfortunately can't test the PATCH endpoint as it requires the subscription being in "issued" state. However, we can at least test that the resource correctly decides not to use the PATCH endpoint as a result and instead recreates the resource.

* Add force_update argument to opt-in to potentially dangerous operation

Same logic as the force_destroy argument.

* Rename test variables for clarity
  • Loading branch information
bengesoff authored Apr 6, 2021
1 parent cd24eda commit edde46c
Show file tree
Hide file tree
Showing 16 changed files with 375 additions and 21 deletions.
4 changes: 3 additions & 1 deletion docs/resources/tls_subscription.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,10 @@ The following arguments are supported:
* `domains` - (Required) List of domains on which to enable TLS.
* `certificate_authority` - (Required) The entity that issues and certifies the TLS certificates for your subscription. Valid values are `lets-encrypt` or `globalsign`.
* `configuration_id` - (Optional) The ID of the set of TLS configuration options that apply to the enabled domains on this subscription.
* `force_update` - (Optional) Always update subscription, even when active domains are present. Defaults to false.
* `force_destroy` - (Optional) Always delete subscription, even when active domains are present. Defaults to false.

!> **Warning:** by default, the Fastly API protects you from disabling production traffic by preventing deleting subscriptions with active domains. The use of `force_destroy` will override these protections. Take extra care using this option if you are handling production traffic.
!> **Warning:** by default, the Fastly API protects you from disabling production traffic by preventing updating or deleting subscriptions with active domains. The use of `force_update` and `force_destroy` will override these protections. Take extra care using these options if you are handling production traffic.

## Attributes Reference

Expand Down Expand Up @@ -153,6 +154,7 @@ $ terraform import fastly_tls_subscription.demo xxxxxxxxxxx
- **common_name** (String) The common name associated with the subscription generated by Fastly TLS. If you do not pass a common name on create, we will default to the first TLS domain included. If provided, the domain chosen as the common name must be included in TLS domains.
- **configuration_id** (String) The ID of the set of TLS configuration options that apply to the enabled domains on this subscription.
- **force_destroy** (Boolean) Force delete the subscription even if it has active domains. Warning: this can disable production traffic if used incorrectly. Defaults to false.
- **force_update** (Boolean) Force update the subscription even if it has active domains. Warning: this can disable production traffic if used incorrectly.
- **id** (String) The ID of this resource.

### Read-Only
Expand Down
52 changes: 48 additions & 4 deletions fastly/resource_fastly_tls_subscription.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"time"

"github.com/fastly/go-fastly/v3/fastly"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)
Expand All @@ -14,16 +15,22 @@ func resourceFastlyTLSSubscription() *schema.Resource {
return &schema.Resource{
CreateContext: resourceFastlyTLSSubscriptionCreate,
ReadContext: resourceFastlyTLSSubscriptionRead,
UpdateContext: resourceFastlyTLSSubscriptionUpdate,
DeleteContext: resourceFastlyTLSSubscriptionDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
// Subscription can only be updated when in "issued" state, otherwise needs to delete/recreate
CustomizeDiff: customdiff.All(
customdiff.ForceNewIf("configuration_id", resourceFastlyTLSSubscriptionStateNotIssued),
customdiff.ForceNewIf("domains", resourceFastlyTLSSubscriptionStateNotIssued),
customdiff.ForceNewIf("common_name", resourceFastlyTLSSubscriptionStateNotIssued),
),
Schema: map[string]*schema.Schema{
"domains": {
Type: schema.TypeSet,
Description: "List of domains on which to enable TLS.",
Required: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
MinItems: 1,
},
Expand All @@ -32,7 +39,6 @@ func resourceFastlyTLSSubscription() *schema.Resource {
Description: "The common name associated with the subscription generated by Fastly TLS. If you do not pass a common name on create, we will default to the first TLS domain included. If provided, the domain chosen as the common name must be included in TLS domains.",
Optional: true,
Computed: true,
ForceNew: true,
},
"certificate_authority": {
Type: schema.TypeString,
Expand All @@ -46,7 +52,6 @@ func resourceFastlyTLSSubscription() *schema.Resource {
Description: "The ID of the set of TLS configuration options that apply to the enabled domains on this subscription.",
Optional: true,
Computed: true,
ForceNew: true,
},
"created_at": {
Type: schema.TypeString,
Expand Down Expand Up @@ -98,7 +103,12 @@ func resourceFastlyTLSSubscription() *schema.Resource {
Type: schema.TypeBool,
Description: "Force delete the subscription even if it has active domains. Warning: this can disable production traffic if used incorrectly. Defaults to false.",
Optional: true,
ForceNew: true, // FIXME: remove when PATCH endpoint included in Update function
Default: false,
},
"force_update": {
Type: schema.TypeBool,
Description: "Force update the subscription even if it has active domains. Warning: this can disable production traffic if used incorrectly.",
Optional: true,
Default: false,
},
},
Expand Down Expand Up @@ -223,6 +233,36 @@ func resourceFastlyTLSSubscriptionRead(_ context.Context, d *schema.ResourceData
return nil
}

func resourceFastlyTLSSubscriptionUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
conn := meta.(*FastlyClient).conn

updates := &fastly.UpdateTLSSubscriptionInput{
ID: d.Id(),
Force: d.Get("force_update").(bool),
}

if d.HasChange("domains") {
var domains []*fastly.TLSDomain
for _, domain := range d.Get("domains").(*schema.Set).List() {
domains = append(domains, &fastly.TLSDomain{ID: domain.(string)})
}
updates.Domains = domains
}
if d.HasChange("common_name") {
updates.CommonName = &fastly.TLSDomain{ID: d.Get("common_name").(string)}
}
if d.HasChange("configuration_id") {
updates.Configuration = &fastly.TLSConfiguration{ID: d.Get("configuration_id").(string)}
}

_, err := conn.UpdateTLSSubscription(updates)
if err != nil {
return diag.FromErr(err)
}

return resourceFastlyTLSSubscriptionRead(ctx, d, meta)
}

func resourceFastlyTLSSubscriptionDelete(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
conn := meta.(*FastlyClient).conn

Expand All @@ -232,3 +272,7 @@ func resourceFastlyTLSSubscriptionDelete(_ context.Context, d *schema.ResourceDa
})
return diag.FromErr(err)
}

func resourceFastlyTLSSubscriptionStateNotIssued(_ context.Context, d *schema.ResourceDiff, _ interface{}) bool {
return d.Get("state").(string) != "issued"
}
24 changes: 18 additions & 6 deletions fastly/resource_fastly_tls_subscription_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ func init() {

func TestAccResourceFastlyTLSSubscription(t *testing.T) {
name := acctest.RandomWithPrefix(testResourcePrefix)
domain := fmt.Sprintf("%s.test", name)
domain1 := fmt.Sprintf("%s.test", name)
domain2 := fmt.Sprintf("%sALT.test", name)
commonName1 := domain1
commonName2 := domain2

resourceName := "fastly_tls_subscription.subject"
resource.ParallelTest(t, resource.TestCase{
Expand All @@ -30,23 +33,27 @@ func TestAccResourceFastlyTLSSubscription(t *testing.T) {
CheckDestroy: testAccCheckServiceV1Destroy,
Steps: []resource.TestStep{
{
Config: testAccResourceFastlyTLSSubscriptionConfig(name, domain),
Config: testAccResourceFastlyTLSSubscriptionConfig(name, domain1, domain2, commonName1),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet(resourceName, "configuration_id"),
resource.TestCheckResourceAttrSet(resourceName, "created_at"),
resource.TestCheckResourceAttrSet(resourceName, "updated_at"),
resource.TestCheckResourceAttrSet(resourceName, "state"),
resource.TestCheckResourceAttr(resourceName, "managed_dns_challenge.%", "3"),
resource.TestCheckResourceAttrSet(resourceName, "managed_http_challenges.#"),
resource.TestCheckResourceAttr(resourceName, "common_name", domain),
resource.TestCheckResourceAttr(resourceName, "common_name", domain1),
testAccResourceFastlyTLSSubscriptionExists(resourceName),
),
},
{
Config: testAccResourceFastlyTLSSubscriptionConfig(name, domain1, domain2, commonName2),
Check: resource.TestCheckResourceAttr(resourceName, "common_name", domain2),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"force_destroy"},
ImportStateVerifyIgnore: []string{"force_destroy", "force_update"},
},
{
Config: testAccResourceFastlyTLSSubscriptionConfig_invalidCommonName(),
Expand All @@ -56,7 +63,7 @@ func TestAccResourceFastlyTLSSubscription(t *testing.T) {
})
}

func testAccResourceFastlyTLSSubscriptionConfig(name, domain string) string {
func testAccResourceFastlyTLSSubscriptionConfig(name, domain1, domain2, commonName string) string {
return fmt.Sprintf(`
resource "fastly_service_v1" "test" {
name = "%s"
Expand All @@ -65,6 +72,10 @@ resource "fastly_service_v1" "test" {
name = "%s"
}
domain {
name = "%s"
}
backend {
address = "127.0.0.1"
name = "localhost"
Expand All @@ -74,9 +85,10 @@ resource "fastly_service_v1" "test" {
}
resource "fastly_tls_subscription" "subject" {
domains = [for domain in fastly_service_v1.test.domain : domain.name]
common_name = "%s"
certificate_authority = "lets-encrypt"
}
`, name, domain)
`, name, domain1, domain2, commonName)
}

func testAccResourceFastlyTLSSubscriptionExists(resourceName string) resource.TestCheckFunc {
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.14
require (
github.com/ajg/form v0.0.0-20160822230020-523a5da1a92f // indirect
github.com/bflad/tfproviderlint v0.22.0
github.com/fastly/go-fastly/v3 v3.3.0
github.com/fastly/go-fastly/v3 v3.4.1
github.com/google/go-cmp v0.5.2
github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320
github.com/hashicorp/terraform-plugin-docs v0.4.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fastly/go-fastly/v3 v3.3.0 h1:5rkjRbT6eqmatRRzBNv1MoDSK9hgTlhDo4WWnSyWMc4=
github.com/fastly/go-fastly/v3 v3.3.0/go.mod h1:KOaCWsmkIKSASPzADl8PT/bTQIghOw/eEaxlHOu3jMA=
github.com/fastly/go-fastly/v3 v3.4.1 h1:ljska0Cc2rwloXZsrgMCqm2JsnoGHC5i9A2KmTYqrL4=
github.com/fastly/go-fastly/v3 v3.4.1/go.mod h1:KOaCWsmkIKSASPzADl8PT/bTQIghOw/eEaxlHOu3jMA=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
Expand Down
3 changes: 2 additions & 1 deletion templates/resources/tls_subscription.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,10 @@ The following arguments are supported:
* `domains` - (Required) List of domains on which to enable TLS.
* `certificate_authority` - (Required) The entity that issues and certifies the TLS certificates for your subscription. Valid values are `lets-encrypt` or `globalsign`.
* `configuration_id` - (Optional) The ID of the set of TLS configuration options that apply to the enabled domains on this subscription.
* `force_update` - (Optional) Always update subscription, even when active domains are present. Defaults to false.
* `force_destroy` - (Optional) Always delete subscription, even when active domains are present. Defaults to false.

!> **Warning:** by default, the Fastly API protects you from disabling production traffic by preventing deleting subscriptions with active domains. The use of `force_destroy` will override these protections. Take extra care using this option if you are handling production traffic.
!> **Warning:** by default, the Fastly API protects you from disabling production traffic by preventing updating or deleting subscriptions with active domains. The use of `force_update` and `force_destroy` will override these protections. Take extra care using these options if you are handling production traffic.

## Attributes Reference

Expand Down
2 changes: 1 addition & 1 deletion vendor/github.com/fastly/go-fastly/v3/fastly/client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions vendor/github.com/fastly/go-fastly/v3/fastly/purge.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

45 changes: 44 additions & 1 deletion vendor/github.com/fastly/go-fastly/v3/fastly/tls_subscription.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit edde46c

Please sign in to comment.