From 6eebaf9e508e16b60155c46e2bbdaf908e5fbc76 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Fri, 23 Sep 2022 09:55:11 +0100 Subject: [PATCH 1/9] Adding call to modifyStateIfCertificateReadyForRenewal from Read func in locally_signed_cert and self_signed_cert resources (#268) --- internal/provider/common_cert.go | 45 +++++++++++++++++++ .../provider/resource_locally_signed_cert.go | 6 ++- .../provider/resource_self_signed_cert.go | 6 ++- 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/internal/provider/common_cert.go b/internal/provider/common_cert.go index 0aa4bfd7..c225475a 100644 --- a/internal/provider/common_cert.go +++ b/internal/provider/common_cert.go @@ -296,6 +296,51 @@ func modifyPlanIfCertificateReadyForRenewal(ctx context.Context, req *resource.M } } +func modifyStateIfCertificateReadyForRenewal(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + // Retrieve `validity_end_time` and confirm is a known, non-null value + validityEndTimePath := path.Root("validity_end_time") + var validityEndTimeStr types.String + resp.Diagnostics.Append(req.State.GetAttribute(ctx, validityEndTimePath, &validityEndTimeStr)...) + if resp.Diagnostics.HasError() { + return + } + if validityEndTimeStr.IsNull() || validityEndTimeStr.IsUnknown() { + return + } + + // Parse `validity_end_time` + validityEndTime, err := time.Parse(time.RFC3339, validityEndTimeStr.Value) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Failed to parse data from string: %s", validityEndTimeStr.Value), + err.Error(), + ) + return + } + + // Retrieve `early_renewal_hours` + earlyRenewalHoursPath := path.Root("early_renewal_hours") + var earlyRenewalHours int64 + resp.Diagnostics.Append(req.State.GetAttribute(ctx, earlyRenewalHoursPath, &earlyRenewalHours)...) + if resp.Diagnostics.HasError() { + return + } + + currentTime := overridableTimeFunc() + + // Determine the time from which an "early renewal" is possible + earlyRenewalPeriod := time.Duration(-earlyRenewalHours) * time.Hour + earlyRenewalTime := validityEndTime.Add(earlyRenewalPeriod) + + // If "early renewal" time has passed, mark it "ready for renewal" + timeToEarlyRenewal := earlyRenewalTime.Sub(currentTime) + if timeToEarlyRenewal <= 0 { + tflog.Info(ctx, "Certificate is ready for early renewal") + readyForRenewalPath := path.Root("ready_for_renewal") + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, readyForRenewalPath, true)...) + } +} + // createSubjectDistinguishedNames return a *pkix.Name (i.e. a "Certificate Subject") if the non-empty. // This used for creating x509.Certificate or x509.CertificateRequest. func createSubjectDistinguishedNames(ctx context.Context, subject certificateSubjectModel) pkix.Name { diff --git a/internal/provider/resource_locally_signed_cert.go b/internal/provider/resource_locally_signed_cert.go index e69f3c9f..711cec6e 100644 --- a/internal/provider/resource_locally_signed_cert.go +++ b/internal/provider/resource_locally_signed_cert.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-provider-tls/internal/provider/attribute_plan_modifier" ) @@ -275,9 +276,10 @@ func (r *locallySignedCertResource) Create(ctx context.Context, req resource.Cre res.Diagnostics.Append(res.State.Set(ctx, newState)...) } -func (r *locallySignedCertResource) Read(ctx context.Context, _ resource.ReadRequest, _ *resource.ReadResponse) { - // NO-OP: all there is to read is in the State, and response is already populated with that. +func (r *locallySignedCertResource) Read(ctx context.Context, req resource.ReadRequest, res *resource.ReadResponse) { tflog.Debug(ctx, "Reading locally signed certificate from state") + + modifyStateIfCertificateReadyForRenewal(ctx, req, res) } func (r *locallySignedCertResource) Update(ctx context.Context, req resource.UpdateRequest, res *resource.UpdateResponse) { diff --git a/internal/provider/resource_self_signed_cert.go b/internal/provider/resource_self_signed_cert.go index 7c975c0c..ba7c6bdc 100644 --- a/internal/provider/resource_self_signed_cert.go +++ b/internal/provider/resource_self_signed_cert.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-provider-tls/internal/provider/attribute_plan_modifier" ) @@ -431,9 +432,10 @@ func (r *selfSignedCertResource) Create(ctx context.Context, req resource.Create res.Diagnostics.Append(res.State.Set(ctx, newState)...) } -func (r *selfSignedCertResource) Read(ctx context.Context, _ resource.ReadRequest, _ *resource.ReadResponse) { - // NO-OP: all there is to read is in the State, and response is already populated with that. +func (r *selfSignedCertResource) Read(ctx context.Context, req resource.ReadRequest, res *resource.ReadResponse) { tflog.Debug(ctx, "Reading self signed certificate from state") + + modifyStateIfCertificateReadyForRenewal(ctx, req, res) } func (r *selfSignedCertResource) Update(ctx context.Context, req resource.UpdateRequest, res *resource.UpdateResponse) { From f31a5e82dab3fe5312937186bddc4ae256968c55 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Wed, 28 Sep 2022 07:59:31 +0100 Subject: [PATCH 2/9] Set ready_for_renewal to unknown in resource plan modifier (#268) --- internal/provider/common_cert.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/provider/common_cert.go b/internal/provider/common_cert.go index c225475a..c3aed707 100644 --- a/internal/provider/common_cert.go +++ b/internal/provider/common_cert.go @@ -291,7 +291,7 @@ func modifyPlanIfCertificateReadyForRenewal(ctx context.Context, req *resource.M if timeToEarlyRenewal <= 0 { tflog.Info(ctx, "Certificate is ready for early renewal") readyForRenewalPath := path.Root("ready_for_renewal") - res.Diagnostics.Append(res.Plan.SetAttribute(ctx, readyForRenewalPath, true)...) + res.Diagnostics.Append(res.Plan.SetAttribute(ctx, readyForRenewalPath, types.Bool{Unknown: true})...) res.RequiresReplace = append(res.RequiresReplace, readyForRenewalPath) } } From b6550b99eb6274046b0915fe5eb33eef0f521dee Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Thu, 29 Sep 2022 07:37:23 +0100 Subject: [PATCH 3/9] Add CHANGELOG entry (#268) --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10ff096f..119268f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 4.0.4 (unreleased) + +BUG FIXES: + +* resource/tls_locally_signed_cert: Ensure `terraform refresh` updates state when cert is ready for renewal ([#278](https://github.com/hashicorp/terraform-provider-tls/issues/278)). +* resource/tls_self_signed_cert: Ensure `terraform refresh` updates state when cert is ready for renewal ([#278](https://github.com/hashicorp/terraform-provider-tls/issues/278)). + ## 4.0.3 (September 20, 2022) BUG FIXES: From 9115ea85e85047fa5c6a1cf93abb1325712bb85a Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Mon, 3 Oct 2022 03:39:15 +0100 Subject: [PATCH 4/9] Adding tests to verify that a refresh-only terraform apply mutates the state for ready_for_renewal and maintains the requires replace behaviour for terraform apply (#268) --- .../resource_locally_signed_cert_test.go | 54 +++++++++++++++++++ .../resource_self_signed_cert_test.go | 54 +++++++++++++++++++ 2 files changed, 108 insertions(+) diff --git a/internal/provider/resource_locally_signed_cert_test.go b/internal/provider/resource_locally_signed_cert_test.go index af81d7a3..6674fe2b 100644 --- a/internal/provider/resource_locally_signed_cert_test.go +++ b/internal/provider/resource_locally_signed_cert_test.go @@ -200,6 +200,60 @@ func TestResourceLocallySignedCert_DetectExpiringAndExpired(t *testing.T) { overridableTimeFunc = oldNow } +func TestResourceLocallySignedCert_DetectExpiring_Refresh(t *testing.T) { + oldNow := overridableTimeFunc + r.UnitTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + PreCheck: setTimeForTest("2019-06-14T12:00:00Z"), + Steps: []r.TestStep{ + { + Config: locallySignedCertConfig(10, 2), + Check: r.TestCheckResourceAttr("tls_locally_signed_cert.test", "ready_for_renewal", "false"), + }, + { + PreConfig: setTimeForTest("2019-06-14T21:30:00Z"), + Config: locallySignedCertConfig(10, 2), + RefreshState: true, + Check: r.TestCheckResourceAttr("tls_locally_signed_cert.test", "ready_for_renewal", "true"), + }, + { + PreConfig: setTimeForTest("2019-06-14T21:30:00Z"), + Config: locallySignedCertConfig(10, 2), + PlanOnly: true, + ExpectNonEmptyPlan: true, + }, + }, + }) + overridableTimeFunc = oldNow +} + +func TestResourceLocallySignedCert_DetectExpired_Refresh(t *testing.T) { + oldNow := overridableTimeFunc + r.UnitTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + PreCheck: setTimeForTest("2019-06-14T12:00:00Z"), + Steps: []r.TestStep{ + { + Config: locallySignedCertConfig(10, 2), + Check: r.TestCheckResourceAttr("tls_locally_signed_cert.test", "ready_for_renewal", "false"), + }, + { + PreConfig: setTimeForTest("2019-06-14T23:30:00Z"), + Config: locallySignedCertConfig(10, 2), + RefreshState: true, + Check: r.TestCheckResourceAttr("tls_locally_signed_cert.test", "ready_for_renewal", "true"), + }, + { + PreConfig: setTimeForTest("2019-06-14T23:30:00Z"), + Config: locallySignedCertConfig(10, 2), + PlanOnly: true, + ExpectNonEmptyPlan: true, + }, + }, + }) + overridableTimeFunc = oldNow +} + func TestResourceLocallySignedCert_RecreatesAfterExpired(t *testing.T) { oldNow := overridableTimeFunc var previousCert string diff --git a/internal/provider/resource_self_signed_cert_test.go b/internal/provider/resource_self_signed_cert_test.go index 721307bd..d8c9a43b 100644 --- a/internal/provider/resource_self_signed_cert_test.go +++ b/internal/provider/resource_self_signed_cert_test.go @@ -218,6 +218,60 @@ func TestResourceSelfSignedCert_DetectExpiringAndExpired(t *testing.T) { overridableTimeFunc = oldNow } +func TestResourceSelfSignedCert_DetectExpiring_Refresh(t *testing.T) { + oldNow := overridableTimeFunc + r.UnitTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + PreCheck: setTimeForTest("2019-06-14T12:00:00Z"), + Steps: []r.TestStep{ + { + Config: selfSignedCertConfig(10, 2), + Check: r.TestCheckResourceAttr("tls_self_signed_cert.test1", "ready_for_renewal", "false"), + }, + { + PreConfig: setTimeForTest("2019-06-14T21:30:00Z"), + Config: selfSignedCertConfig(10, 2), + RefreshState: true, + Check: r.TestCheckResourceAttr("tls_self_signed_cert.test1", "ready_for_renewal", "true"), + }, + { + PreConfig: setTimeForTest("2019-06-14T21:30:00Z"), + Config: selfSignedCertConfig(10, 2), + PlanOnly: true, + ExpectNonEmptyPlan: true, + }, + }, + }) + overridableTimeFunc = oldNow +} + +func TestResourceSelfSignedCert_DetectExpired_Refresh(t *testing.T) { + oldNow := overridableTimeFunc + r.UnitTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + PreCheck: setTimeForTest("2019-06-14T12:00:00Z"), + Steps: []r.TestStep{ + { + Config: selfSignedCertConfig(10, 2), + Check: r.TestCheckResourceAttr("tls_self_signed_cert.test1", "ready_for_renewal", "false"), + }, + { + PreConfig: setTimeForTest("2019-06-14T23:30:00Z"), + Config: selfSignedCertConfig(10, 2), + RefreshState: true, + Check: r.TestCheckResourceAttr("tls_self_signed_cert.test1", "ready_for_renewal", "true"), + }, + { + PreConfig: setTimeForTest("2019-06-14T23:30:00Z"), + Config: selfSignedCertConfig(10, 2), + PlanOnly: true, + ExpectNonEmptyPlan: true, + }, + }, + }) + overridableTimeFunc = oldNow +} + func TestResourceSelfSignedCert_RecreatesAfterExpired(t *testing.T) { oldNow := overridableTimeFunc var previousCert string From 60601c7a3cedceb0902251a70af1b87263fcdb5f Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Wed, 5 Oct 2022 12:01:54 +0100 Subject: [PATCH 5/9] Adding plan modifier to set ready_for_renewal true in state if validity_period_hours is zero or early_renewal_hours is greater than or equal to validity_period_hours at time of locally or self-signed cert creation (#268) --- .../attribute_plan_modifier/default_value.go | 57 +++++++++++++++++++ .../provider/resource_locally_signed_cert.go | 19 +++++++ .../resource_locally_signed_cert_test.go | 48 ++++++++++++++++ .../provider/resource_self_signed_cert.go | 19 +++++++ .../resource_self_signed_cert_test.go | 48 ++++++++++++++++ 5 files changed, 191 insertions(+) diff --git a/internal/provider/attribute_plan_modifier/default_value.go b/internal/provider/attribute_plan_modifier/default_value.go index e48c060e..151369a4 100644 --- a/internal/provider/attribute_plan_modifier/default_value.go +++ b/internal/provider/attribute_plan_modifier/default_value.go @@ -5,7 +5,9 @@ import ( "fmt" "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" ) // defaultValueAttributePlanModifier specifies a default value (attr.Value) for an attribute. @@ -42,3 +44,58 @@ func (apm *defaultValueAttributePlanModifier) Modify(_ context.Context, req tfsd res.AttributePlan = apm.DefaultValue } + +// readyForRenewalAttributePlanModifier determines whether the certificate is ready for renewal. +type readyForRenewalAttributePlanModifier struct { +} + +// ReadyForRenewal is an helper to instantiate a defaultValueAttributePlanModifier. +func ReadyForRenewal() tfsdk.AttributePlanModifier { + return &readyForRenewalAttributePlanModifier{} +} + +var _ tfsdk.AttributePlanModifier = (*readyForRenewalAttributePlanModifier)(nil) + +func (apm *readyForRenewalAttributePlanModifier) Description(ctx context.Context) string { + return apm.MarkdownDescription(ctx) +} + +func (apm *readyForRenewalAttributePlanModifier) MarkdownDescription(ctx context.Context) string { + return "Sets the value of ready_for_renewal depending on value of validity_period_hours and early_renewal_hours" +} + +func (apm *readyForRenewalAttributePlanModifier) Modify(ctx context.Context, req tfsdk.ModifyAttributePlanRequest, res *tfsdk.ModifyAttributePlanResponse) { + var validityPeriodHours types.Int64 + + res.Diagnostics.Append(req.Plan.GetAttribute(ctx, path.Root("validity_period_hours"), &validityPeriodHours)...) + if res.Diagnostics.HasError() { + return + } + + if validityPeriodHours.Value == 0 { + res.AttributePlan = types.Bool{ + Value: true, + } + + return + } + + var earlyRenewalHours types.Int64 + + res.Diagnostics.Append(req.Plan.GetAttribute(ctx, path.Root("early_renewal_hours"), &earlyRenewalHours)...) + if res.Diagnostics.HasError() { + return + } + + if earlyRenewalHours.IsNull() || earlyRenewalHours.IsUnknown() { + return + } + + if earlyRenewalHours.Value >= validityPeriodHours.Value { + res.AttributePlan = types.Bool{ + Value: true, + } + + return + } +} diff --git a/internal/provider/resource_locally_signed_cert.go b/internal/provider/resource_locally_signed_cert.go index 711cec6e..08a5f37c 100644 --- a/internal/provider/resource_locally_signed_cert.go +++ b/internal/provider/resource_locally_signed_cert.go @@ -158,6 +158,7 @@ func (r *locallySignedCertResource) GetSchema(_ context.Context) (tfsdk.Schema, Computed: true, PlanModifiers: []tfsdk.AttributePlanModifier{ attribute_plan_modifier.DefaultValue(types.Bool{Value: false}), + attribute_plan_modifier.ReadyForRenewal(), }, Description: "Is the certificate either expired (i.e. beyond the `validity_period_hours`) " + "or ready for an early renewal (i.e. within the `early_renewal_hours`)?", @@ -267,6 +268,24 @@ func (r *locallySignedCertResource) Create(ctx context.Context, req resource.Cre return } + // Check whether the certificate is ready for renewal at the time of creation + if newState.EarlyRenewalHours.Value >= newState.ValidityPeriodHours.Value && newState.ValidityPeriodHours.Value > 0 { + tflog.Warn(ctx, "Certificate is ready for renewal at time of creation") + res.Diagnostics.AddWarning( + "Certificate is ready for renewal at time of creation", + fmt.Sprintf("Early renewal hours (%d) is greater than or equal to validity period hours (%d)", newState.EarlyRenewalHours.Value, newState.ValidityPeriodHours.Value), + ) + } + + // Check whether the certificate is already expired at the time of creation + if newState.ValidityPeriodHours.Value == 0 { + tflog.Warn(ctx, "Certificate is already expired at time of creation") + res.Diagnostics.AddWarning( + "Certificate is already expired at time of creation", + "Validity period hours is zero", + ) + } + // Store the certificate into the state tflog.Debug(ctx, "Storing locally signed certificate into the state") newState.ID = types.String{Value: certificate.id} diff --git a/internal/provider/resource_locally_signed_cert_test.go b/internal/provider/resource_locally_signed_cert_test.go index 6674fe2b..fd2a6ff2 100644 --- a/internal/provider/resource_locally_signed_cert_test.go +++ b/internal/provider/resource_locally_signed_cert_test.go @@ -254,6 +254,54 @@ func TestResourceLocallySignedCert_DetectExpired_Refresh(t *testing.T) { overridableTimeFunc = oldNow } +func TestResourceLocallySignedCert_ReadyForRenewal_ValidityPeriodZero(t *testing.T) { + oldNow := overridableTimeFunc + r.UnitTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + PreCheck: setTimeForTest("2019-06-14T12:00:00Z"), + Steps: []r.TestStep{ + { + Config: locallySignedCertConfig(0, 0), + ExpectNonEmptyPlan: true, + Check: r.TestCheckResourceAttr("tls_locally_signed_cert.test", "ready_for_renewal", "true"), + }, + }, + }) + overridableTimeFunc = oldNow +} + +func TestResourceLocallySignedCert_ReadyForRenewal_EarlyRenewalGreaterThanValidityPeriod(t *testing.T) { + oldNow := overridableTimeFunc + r.UnitTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + PreCheck: setTimeForTest("2019-06-14T12:00:00Z"), + Steps: []r.TestStep{ + { + Config: locallySignedCertConfig(1, 2), + ExpectNonEmptyPlan: true, + Check: r.TestCheckResourceAttr("tls_locally_signed_cert.test", "ready_for_renewal", "true"), + }, + }, + }) + overridableTimeFunc = oldNow +} + +func TestResourceLocallySignedCert_ReadyForRenewal_EarlyRenewalEqualsValidityPeriod(t *testing.T) { + oldNow := overridableTimeFunc + r.UnitTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + PreCheck: setTimeForTest("2019-06-14T12:00:00Z"), + Steps: []r.TestStep{ + { + Config: locallySignedCertConfig(1, 1), + ExpectNonEmptyPlan: true, + Check: r.TestCheckResourceAttr("tls_locally_signed_cert.test", "ready_for_renewal", "true"), + }, + }, + }) + overridableTimeFunc = oldNow +} + func TestResourceLocallySignedCert_RecreatesAfterExpired(t *testing.T) { oldNow := overridableTimeFunc var previousCert string diff --git a/internal/provider/resource_self_signed_cert.go b/internal/provider/resource_self_signed_cert.go index ba7c6bdc..a90e0ba1 100644 --- a/internal/provider/resource_self_signed_cert.go +++ b/internal/provider/resource_self_signed_cert.go @@ -181,6 +181,7 @@ func (r *selfSignedCertResource) GetSchema(_ context.Context) (tfsdk.Schema, dia Computed: true, PlanModifiers: []tfsdk.AttributePlanModifier{ attribute_plan_modifier.DefaultValue(types.Bool{Value: false}), + attribute_plan_modifier.ReadyForRenewal(), }, Description: "Is the certificate either expired (i.e. beyond the `validity_period_hours`) " + "or ready for an early renewal (i.e. within the `early_renewal_hours`)?", @@ -423,6 +424,24 @@ func (r *selfSignedCertResource) Create(ctx context.Context, req resource.Create return } + // Check whether the certificate is ready for renewal at the time of creation + if newState.EarlyRenewalHours.Value >= newState.ValidityPeriodHours.Value && newState.ValidityPeriodHours.Value > 0 { + tflog.Warn(ctx, "Certificate is ready for renewal at time of creation") + res.Diagnostics.AddWarning( + "Certificate is ready for renewal at time of creation", + fmt.Sprintf("Early renewal hours (%d) is greater than or equal to validity period hours (%d)", newState.EarlyRenewalHours.Value, newState.ValidityPeriodHours.Value), + ) + } + + // Check whether the certificate is already expired at the time of creation + if newState.ValidityPeriodHours.Value == 0 { + tflog.Warn(ctx, "Certificate is already expired at time of creation") + res.Diagnostics.AddWarning( + "Certificate is already expired at time of creation", + "Validity period hours is zero", + ) + } + // Store the certificate into the state tflog.Debug(ctx, "Storing self signed certificate into the state") newState.ID = types.String{Value: certificate.id} diff --git a/internal/provider/resource_self_signed_cert_test.go b/internal/provider/resource_self_signed_cert_test.go index d8c9a43b..6aed3a13 100644 --- a/internal/provider/resource_self_signed_cert_test.go +++ b/internal/provider/resource_self_signed_cert_test.go @@ -272,6 +272,54 @@ func TestResourceSelfSignedCert_DetectExpired_Refresh(t *testing.T) { overridableTimeFunc = oldNow } +func TestResourceSelfSignedCert_ReadyForRenewal_ValidityPeriodZero(t *testing.T) { + oldNow := overridableTimeFunc + r.UnitTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + PreCheck: setTimeForTest("2019-06-14T12:00:00Z"), + Steps: []r.TestStep{ + { + Config: selfSignedCertConfig(0, 0), + ExpectNonEmptyPlan: true, + Check: r.TestCheckResourceAttr("tls_self_signed_cert.test1", "ready_for_renewal", "true"), + }, + }, + }) + overridableTimeFunc = oldNow +} + +func TestResourceSelfSignedCert_ReadyForRenewal_EarlyRenewalGreaterThanValidityPeriod(t *testing.T) { + oldNow := overridableTimeFunc + r.UnitTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + PreCheck: setTimeForTest("2019-06-14T12:00:00Z"), + Steps: []r.TestStep{ + { + Config: selfSignedCertConfig(1, 2), + ExpectNonEmptyPlan: true, + Check: r.TestCheckResourceAttr("tls_self_signed_cert.test1", "ready_for_renewal", "true"), + }, + }, + }) + overridableTimeFunc = oldNow +} + +func TestResourceSelfSignedCert_ReadyForRenewal_EarlyRenewalEqualsValidityPeriod(t *testing.T) { + oldNow := overridableTimeFunc + r.UnitTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + PreCheck: setTimeForTest("2019-06-14T12:00:00Z"), + Steps: []r.TestStep{ + { + Config: selfSignedCertConfig(1, 1), + ExpectNonEmptyPlan: true, + Check: r.TestCheckResourceAttr("tls_self_signed_cert.test1", "ready_for_renewal", "true"), + }, + }, + }) + overridableTimeFunc = oldNow +} + func TestResourceSelfSignedCert_RecreatesAfterExpired(t *testing.T) { oldNow := overridableTimeFunc var previousCert string From 5cf8851bfb4a7e774c950319c2cd6a8d697a264e Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Thu, 6 Oct 2022 10:32:55 +0100 Subject: [PATCH 6/9] Modifying refresh tests to use the option to check expect non-empty plan added in https://github.com/hashicorp/terraform-plugin-sdk/pull/1070 (#268) --- .../resource_locally_signed_cert_test.go | 16 ++++++++-------- .../provider/resource_self_signed_cert_test.go | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/internal/provider/resource_locally_signed_cert_test.go b/internal/provider/resource_locally_signed_cert_test.go index fd2a6ff2..0fff0b21 100644 --- a/internal/provider/resource_locally_signed_cert_test.go +++ b/internal/provider/resource_locally_signed_cert_test.go @@ -211,10 +211,10 @@ func TestResourceLocallySignedCert_DetectExpiring_Refresh(t *testing.T) { Check: r.TestCheckResourceAttr("tls_locally_signed_cert.test", "ready_for_renewal", "false"), }, { - PreConfig: setTimeForTest("2019-06-14T21:30:00Z"), - Config: locallySignedCertConfig(10, 2), - RefreshState: true, - Check: r.TestCheckResourceAttr("tls_locally_signed_cert.test", "ready_for_renewal", "true"), + PreConfig: setTimeForTest("2019-06-14T21:30:00Z"), + RefreshState: true, + ExpectNonEmptyPlan: true, + Check: r.TestCheckResourceAttr("tls_locally_signed_cert.test", "ready_for_renewal", "true"), }, { PreConfig: setTimeForTest("2019-06-14T21:30:00Z"), @@ -238,10 +238,10 @@ func TestResourceLocallySignedCert_DetectExpired_Refresh(t *testing.T) { Check: r.TestCheckResourceAttr("tls_locally_signed_cert.test", "ready_for_renewal", "false"), }, { - PreConfig: setTimeForTest("2019-06-14T23:30:00Z"), - Config: locallySignedCertConfig(10, 2), - RefreshState: true, - Check: r.TestCheckResourceAttr("tls_locally_signed_cert.test", "ready_for_renewal", "true"), + PreConfig: setTimeForTest("2019-06-14T23:30:00Z"), + RefreshState: true, + ExpectNonEmptyPlan: true, + Check: r.TestCheckResourceAttr("tls_locally_signed_cert.test", "ready_for_renewal", "true"), }, { PreConfig: setTimeForTest("2019-06-14T23:30:00Z"), diff --git a/internal/provider/resource_self_signed_cert_test.go b/internal/provider/resource_self_signed_cert_test.go index 6aed3a13..53ec1b02 100644 --- a/internal/provider/resource_self_signed_cert_test.go +++ b/internal/provider/resource_self_signed_cert_test.go @@ -229,10 +229,10 @@ func TestResourceSelfSignedCert_DetectExpiring_Refresh(t *testing.T) { Check: r.TestCheckResourceAttr("tls_self_signed_cert.test1", "ready_for_renewal", "false"), }, { - PreConfig: setTimeForTest("2019-06-14T21:30:00Z"), - Config: selfSignedCertConfig(10, 2), - RefreshState: true, - Check: r.TestCheckResourceAttr("tls_self_signed_cert.test1", "ready_for_renewal", "true"), + PreConfig: setTimeForTest("2019-06-14T21:30:00Z"), + RefreshState: true, + ExpectNonEmptyPlan: true, + Check: r.TestCheckResourceAttr("tls_self_signed_cert.test1", "ready_for_renewal", "true"), }, { PreConfig: setTimeForTest("2019-06-14T21:30:00Z"), @@ -256,10 +256,10 @@ func TestResourceSelfSignedCert_DetectExpired_Refresh(t *testing.T) { Check: r.TestCheckResourceAttr("tls_self_signed_cert.test1", "ready_for_renewal", "false"), }, { - PreConfig: setTimeForTest("2019-06-14T23:30:00Z"), - Config: selfSignedCertConfig(10, 2), - RefreshState: true, - Check: r.TestCheckResourceAttr("tls_self_signed_cert.test1", "ready_for_renewal", "true"), + PreConfig: setTimeForTest("2019-06-14T23:30:00Z"), + RefreshState: true, + ExpectNonEmptyPlan: true, + Check: r.TestCheckResourceAttr("tls_self_signed_cert.test1", "ready_for_renewal", "true"), }, { PreConfig: setTimeForTest("2019-06-14T23:30:00Z"), From 931121f45cd728e74cf511b03418a5c882952ebb Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Tue, 11 Oct 2022 07:26:57 +0100 Subject: [PATCH 7/9] Removing warning diagnostics generated when locally signed or self signed certificates are ready for renewal or have a validity period of zero at the time they are created (#268) Reference: https://github.com/hashicorp/terraform-provider-tls/issues/282 --- .../provider/resource_locally_signed_cert.go | 18 ------------------ internal/provider/resource_self_signed_cert.go | 18 ------------------ 2 files changed, 36 deletions(-) diff --git a/internal/provider/resource_locally_signed_cert.go b/internal/provider/resource_locally_signed_cert.go index 08a5f37c..b1e6d324 100644 --- a/internal/provider/resource_locally_signed_cert.go +++ b/internal/provider/resource_locally_signed_cert.go @@ -268,24 +268,6 @@ func (r *locallySignedCertResource) Create(ctx context.Context, req resource.Cre return } - // Check whether the certificate is ready for renewal at the time of creation - if newState.EarlyRenewalHours.Value >= newState.ValidityPeriodHours.Value && newState.ValidityPeriodHours.Value > 0 { - tflog.Warn(ctx, "Certificate is ready for renewal at time of creation") - res.Diagnostics.AddWarning( - "Certificate is ready for renewal at time of creation", - fmt.Sprintf("Early renewal hours (%d) is greater than or equal to validity period hours (%d)", newState.EarlyRenewalHours.Value, newState.ValidityPeriodHours.Value), - ) - } - - // Check whether the certificate is already expired at the time of creation - if newState.ValidityPeriodHours.Value == 0 { - tflog.Warn(ctx, "Certificate is already expired at time of creation") - res.Diagnostics.AddWarning( - "Certificate is already expired at time of creation", - "Validity period hours is zero", - ) - } - // Store the certificate into the state tflog.Debug(ctx, "Storing locally signed certificate into the state") newState.ID = types.String{Value: certificate.id} diff --git a/internal/provider/resource_self_signed_cert.go b/internal/provider/resource_self_signed_cert.go index a90e0ba1..5d7d93ac 100644 --- a/internal/provider/resource_self_signed_cert.go +++ b/internal/provider/resource_self_signed_cert.go @@ -424,24 +424,6 @@ func (r *selfSignedCertResource) Create(ctx context.Context, req resource.Create return } - // Check whether the certificate is ready for renewal at the time of creation - if newState.EarlyRenewalHours.Value >= newState.ValidityPeriodHours.Value && newState.ValidityPeriodHours.Value > 0 { - tflog.Warn(ctx, "Certificate is ready for renewal at time of creation") - res.Diagnostics.AddWarning( - "Certificate is ready for renewal at time of creation", - fmt.Sprintf("Early renewal hours (%d) is greater than or equal to validity period hours (%d)", newState.EarlyRenewalHours.Value, newState.ValidityPeriodHours.Value), - ) - } - - // Check whether the certificate is already expired at the time of creation - if newState.ValidityPeriodHours.Value == 0 { - tflog.Warn(ctx, "Certificate is already expired at time of creation") - res.Diagnostics.AddWarning( - "Certificate is already expired at time of creation", - "Validity period hours is zero", - ) - } - // Store the certificate into the state tflog.Debug(ctx, "Storing self signed certificate into the state") newState.ID = types.String{Value: certificate.id} From 284988d2ea9681787f5d5387f8ab4474526d9773 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Thu, 13 Oct 2022 15:51:33 +0100 Subject: [PATCH 8/9] Bumping terraform-plugin-sdk/v2 to v2.24.0 and hcl/v2 to v2.14.1 (#268) --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index c9c9111f..2a094712 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/hashicorp/terraform-plugin-framework-validators v0.5.0 github.com/hashicorp/terraform-plugin-go v0.14.0 github.com/hashicorp/terraform-plugin-log v0.7.0 - github.com/hashicorp/terraform-plugin-sdk/v2 v2.23.0 + github.com/hashicorp/terraform-plugin-sdk/v2 v2.24.0 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa golang.org/x/net v0.0.0-20220728153142-1f511ac62c11 ) @@ -38,7 +38,7 @@ require ( github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/hc-install v0.4.0 // indirect - github.com/hashicorp/hcl/v2 v2.14.0 // indirect + github.com/hashicorp/hcl/v2 v2.14.1 // indirect github.com/hashicorp/logutils v1.0.0 // indirect github.com/hashicorp/terraform-exec v0.17.3 // indirect github.com/hashicorp/terraform-json v0.14.0 // indirect diff --git a/go.sum b/go.sum index a0cbc618..339b550b 100644 --- a/go.sum +++ b/go.sum @@ -134,8 +134,8 @@ github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mO github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/hc-install v0.4.0 h1:cZkRFr1WVa0Ty6x5fTvL1TuO1flul231rWkGH92oYYk= github.com/hashicorp/hc-install v0.4.0/go.mod h1:5d155H8EC5ewegao9A4PUTMNPZaq+TbOzkJJZ4vrXeI= -github.com/hashicorp/hcl/v2 v2.14.0 h1:jX6+Q38Ly9zaAJlAjnFVyeNSNCKKW8D0wvyg7vij5Wc= -github.com/hashicorp/hcl/v2 v2.14.0/go.mod h1:e4z5nxYlWNPdDSNYX+ph14EvWYMFm3eP0zIUqPc2jr0= +github.com/hashicorp/hcl/v2 v2.14.1 h1:x0BpjfZ+CYdbiz+8yZTQ+gdLO7IXvOut7Da+XJayx34= +github.com/hashicorp/hcl/v2 v2.14.1/go.mod h1:e4z5nxYlWNPdDSNYX+ph14EvWYMFm3eP0zIUqPc2jr0= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/terraform-exec v0.17.3 h1:MX14Kvnka/oWGmIkyuyvL6POx25ZmKrjlaclkx3eErU= @@ -152,8 +152,8 @@ github.com/hashicorp/terraform-plugin-go v0.14.0 h1:ttnSlS8bz3ZPYbMb84DpcPhY4F5D github.com/hashicorp/terraform-plugin-go v0.14.0/go.mod h1:2nNCBeRLaenyQEi78xrGrs9hMbulveqG/zDMQSvVJTE= github.com/hashicorp/terraform-plugin-log v0.7.0 h1:SDxJUyT8TwN4l5b5/VkiTIaQgY6R+Y2BQ0sRZftGKQs= github.com/hashicorp/terraform-plugin-log v0.7.0/go.mod h1:p4R1jWBXRTvL4odmEkFfDdhUjHf9zcs/BCoNHAc7IK4= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.23.0 h1:D4EeQm0piYXIHp6ZH3zjyP2Elq6voC64x3GZptaiefA= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.23.0/go.mod h1:xkJGavPvP9kYS/VbiW8o7JuTNgPwm7Tiw/Ie/b46r4c= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.24.0 h1:FtCLTiTcykdsURXPt/ku7fYXm3y19nbzbZcUxHx9RbI= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.24.0/go.mod h1:80wf5oad1tW+oLnbXS4UTYmDCrl7BuN1Q+IA91X1a4Y= github.com/hashicorp/terraform-registry-address v0.0.0-20220623143253-7d51757b572c h1:D8aRO6+mTqHfLsK/BC3j5OAoogv1WLRWzY1AaTo3rBg= github.com/hashicorp/terraform-registry-address v0.0.0-20220623143253-7d51757b572c/go.mod h1:Wn3Na71knbXc1G8Lh+yu/dQWWJeFQEpDeJMtWMtlmNI= github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 h1:HKLsbzeOsfXmKNpr3GiT18XAblV0BjCbzL8KQAMZGa0= From 3a89efb531bfb45158d3af7d42de3ff1698ce264 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Thu, 13 Oct 2022 19:46:06 +0100 Subject: [PATCH 9/9] Amending tests to verify following plan apply no further diff remains (#268) --- .../provider/resource_locally_signed_cert_test.go | 14 ++++++-------- .../provider/resource_self_signed_cert_test.go | 14 ++++++-------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/internal/provider/resource_locally_signed_cert_test.go b/internal/provider/resource_locally_signed_cert_test.go index 0fff0b21..7246d482 100644 --- a/internal/provider/resource_locally_signed_cert_test.go +++ b/internal/provider/resource_locally_signed_cert_test.go @@ -217,10 +217,9 @@ func TestResourceLocallySignedCert_DetectExpiring_Refresh(t *testing.T) { Check: r.TestCheckResourceAttr("tls_locally_signed_cert.test", "ready_for_renewal", "true"), }, { - PreConfig: setTimeForTest("2019-06-14T21:30:00Z"), - Config: locallySignedCertConfig(10, 2), - PlanOnly: true, - ExpectNonEmptyPlan: true, + PreConfig: setTimeForTest("2019-06-14T21:30:00Z"), + Config: locallySignedCertConfig(10, 2), + Check: r.TestCheckResourceAttr("tls_locally_signed_cert.test", "ready_for_renewal", "false"), }, }, }) @@ -244,10 +243,9 @@ func TestResourceLocallySignedCert_DetectExpired_Refresh(t *testing.T) { Check: r.TestCheckResourceAttr("tls_locally_signed_cert.test", "ready_for_renewal", "true"), }, { - PreConfig: setTimeForTest("2019-06-14T23:30:00Z"), - Config: locallySignedCertConfig(10, 2), - PlanOnly: true, - ExpectNonEmptyPlan: true, + PreConfig: setTimeForTest("2019-06-14T23:30:00Z"), + Config: locallySignedCertConfig(10, 2), + Check: r.TestCheckResourceAttr("tls_locally_signed_cert.test", "ready_for_renewal", "false"), }, }, }) diff --git a/internal/provider/resource_self_signed_cert_test.go b/internal/provider/resource_self_signed_cert_test.go index 53ec1b02..ff213ca0 100644 --- a/internal/provider/resource_self_signed_cert_test.go +++ b/internal/provider/resource_self_signed_cert_test.go @@ -235,10 +235,9 @@ func TestResourceSelfSignedCert_DetectExpiring_Refresh(t *testing.T) { Check: r.TestCheckResourceAttr("tls_self_signed_cert.test1", "ready_for_renewal", "true"), }, { - PreConfig: setTimeForTest("2019-06-14T21:30:00Z"), - Config: selfSignedCertConfig(10, 2), - PlanOnly: true, - ExpectNonEmptyPlan: true, + PreConfig: setTimeForTest("2019-06-14T21:30:00Z"), + Config: selfSignedCertConfig(10, 2), + Check: r.TestCheckResourceAttr("tls_self_signed_cert.test1", "ready_for_renewal", "false"), }, }, }) @@ -262,10 +261,9 @@ func TestResourceSelfSignedCert_DetectExpired_Refresh(t *testing.T) { Check: r.TestCheckResourceAttr("tls_self_signed_cert.test1", "ready_for_renewal", "true"), }, { - PreConfig: setTimeForTest("2019-06-14T23:30:00Z"), - Config: selfSignedCertConfig(10, 2), - PlanOnly: true, - ExpectNonEmptyPlan: true, + PreConfig: setTimeForTest("2019-06-14T23:30:00Z"), + Config: selfSignedCertConfig(10, 2), + Check: r.TestCheckResourceAttr("tls_self_signed_cert.test1", "ready_for_renewal", "false"), }, }, })