Skip to content

Commit

Permalink
SDKv2 to Framework Migration (#112)
Browse files Browse the repository at this point in the history
* set up muxing to prepare for migration

* update dependencies

* Simplify muxing

* Setting up muxing for tests

* Start migrating time offset resource to framework

* Implement remaining offset attributes in import function

* refactor tests

* Remove unneeded test function

* add schema validators to offset_days field

* Implement update function

* tidy the go mod

* add pointer receiver to functions implementing framework interfaces

* add helper method to convert offset to Int64 type

* refactor time offset multiplication

* add tests to check updated offset values

* Add 'AtLeastOneOf' validators to offset attributes

* Create setOffsetValues helper method

* Begin migrating time_rotating resource to framework

* Implement AtLeastOneOf and int64 AtLeast validators and Create function

* add Plan Modifiers to offset resource

* add Return statements after offset parsing error handling

* Replace !=0 with >0 for offset import checks

* Implement import function

* Add description to offset and rotating schemas

* Add check to ensure that plan modifier is only run when at least one of the offset_* fields has been updated (#51)

* Add test to verify behavior of time_offset resource is not altered by migrating from SDKv2 to the Framework (#51)

* Update error message

* Implement update and partially implement read

* Refactor ModifyPlan and Import functions to use setOffsetValues helper function

* Partially implement modifyPlan function

* Implement IsRFC3339Time validator

* Start implementation of time_sleep resource

* Implement schema for time_sleep resource

* Implement replaceIfOutdated plan modifier

* Finish read implementation for time_rotating

* fix bug to remove resource after time expiration in time_rotating resource

* implement newResource and CRUD operations for time_sleep resource

* Remove unneeded code in delete method

* add Update implementation to pass values from plan to the state

* Start time_static resource migration

* Implement schema for time_static resource

* Implement importState function for time_static resource

* Implement CRUD operations for time_static resource

* Update importState implementation to populate schema fields

* Refactor time_rotating tests

* Refactor time_sleep tests

* Refactor time_static tests

* Remove SDKv2 versions of provider and resources and move tests to provider directory

* Remove provider muxer

* Address linter warnings

* Upgrade terraform-plugin-framework version to 0.13.0

* Refactor ineffectual assignments

* Add markdown description for static resource

* Move Metadata and GetSchema functions to the top of each resource

* Refactor provider name in order to remove of import alias

* Refactor hardcoded value for attribute path

* Add checks to return early if diagnostics has error

* Refactor setOffsetValues to mutate the plan

* Refactor helper functions to return errors

* Clean up syntax

* Add tests to check if resource has/hasn't been recreated

* Set unparam linter to ignore test function

* add Terraform version 1.3.* testing

* Adding a code comment

* Add defensive check for a null Plan in modifyPlan functions

* Move offset attribute validators to a ConfigValidators method

* Refactor tests to fix typos and add back previously removed test coverage

* Refactor upgrade tests to be acceptance tests instead of unit tests

* Move rotating config validations outside of attribute validation to configValidators method

* Refactor to check for null and unknown values by checking attr.Value directly

* Update comment for clarity

* Update regex for validator test

* Update CHANGELOG.md

Co-authored-by: Benjamin Bennett <[email protected]>
  • Loading branch information
SBGoods and bendbennett authored Oct 11, 2022
1 parent 1dc6bcb commit ea34431
Show file tree
Hide file tree
Showing 23 changed files with 2,482 additions and 1,445 deletions.
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ jobs:
- '1.0.*'
- '1.1.*'
- '1.2.*'
- '1.3.*'

steps:

Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 0.9.0 (October 11, 2022)

NOTES:

* provider: Rewritten to use the new [`terraform-plugin-framework`](https://www.terraform.io/plugin/framework) ([#112](https://github.com/hashicorp/terraform-provider-time/pull/112))

## 0.8.0 (August 10, 2022)

BUG FIXES:
Expand Down
12 changes: 7 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ go 1.18

require (
github.com/hashicorp/terraform-plugin-docs v0.13.0
github.com/hashicorp/terraform-plugin-framework v0.13.0
github.com/hashicorp/terraform-plugin-framework-validators v0.5.0
github.com/hashicorp/terraform-plugin-go v0.14.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.23.0
)

Expand Down Expand Up @@ -34,7 +37,6 @@ require (
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
github.com/hashicorp/terraform-plugin-go v0.14.0 // indirect
github.com/hashicorp/terraform-plugin-log v0.7.0 // indirect
github.com/hashicorp/terraform-registry-address v0.0.0-20220623143253-7d51757b572c // indirect
github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 // indirect
Expand All @@ -56,13 +58,13 @@ require (
github.com/spf13/cast v1.5.0 // indirect
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect
github.com/vmihailenco/tagparser v0.1.1 // indirect
github.com/vmihailenco/tagparser v0.1.2 // indirect
github.com/zclconf/go-cty v1.11.0 // indirect
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b // indirect
golang.org/x/net v0.0.0-20220708220712-1185a9018129 // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/appengine v1.6.6 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20200711021454-869866162049 // indirect
google.golang.org/grpc v1.48.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
Expand Down
19 changes: 12 additions & 7 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ github.com/hashicorp/terraform-json v0.14.0 h1:sh9iZ1Y8IFJLx+xQiKHGud6/TSUCM0N8e
github.com/hashicorp/terraform-json v0.14.0/go.mod h1:5A9HIWPkk4e5aeeXIBbkcOvaZbIYnAIkEyqP2pNSckM=
github.com/hashicorp/terraform-plugin-docs v0.13.0 h1:6e+VIWsVGb6jYJewfzq2ok2smPzZrt1Wlm9koLeKazY=
github.com/hashicorp/terraform-plugin-docs v0.13.0/go.mod h1:W0oCmHAjIlTHBbvtppWHe8fLfZ2BznQbuv8+UD8OucQ=
github.com/hashicorp/terraform-plugin-framework v0.13.0 h1:tGnqttzZwU3FKc+HasHr2Yi5L81FcQbdc8zQhbBD9jQ=
github.com/hashicorp/terraform-plugin-framework v0.13.0/go.mod h1:wcZdk4+Uef6Ng+BiBJjGAcIPlIs5bhlEV/TA1k6Xkq8=
github.com/hashicorp/terraform-plugin-framework-validators v0.5.0 h1:eD79idhnJOBajkUMEbm0c8dOyOb/F49STbUEVojT6F4=
github.com/hashicorp/terraform-plugin-framework-validators v0.5.0/go.mod h1:NfGgclDM3FZqvNVppPKE2aHI1JAyT002ypPRya7ch3I=
github.com/hashicorp/terraform-plugin-go v0.14.0 h1:ttnSlS8bz3ZPYbMb84DpcPhY4F5DsQtcAS7cHo8uvP4=
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=
Expand Down Expand Up @@ -240,8 +244,9 @@ github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaU
github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
github.com/vmihailenco/msgpack/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvCazn8G65U=
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY=
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc=
github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
Expand Down Expand Up @@ -280,8 +285,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220708220712-1185a9018129 h1:vucSRfWwTsoXro7P+3Cjlr6flUMtzCwzlvkxEQtHHB0=
golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
Expand Down Expand Up @@ -309,8 +314,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b h1:2n253B2r0pYSmEV+UNCQoPfU/FiaizQEK5Gu4Bq4JE8=
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
Expand All @@ -328,8 +333,8 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
Expand Down
70 changes: 70 additions & 0 deletions internal/modifiers/timemodifier/replace_if_outdated.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package timemodifier

import (
"context"
"fmt"
"time"

"github.com/hashicorp/terraform-plugin-framework/tfsdk"
"github.com/hashicorp/terraform-plugin-framework/types"
)

func ReplaceIfOutdated() tfsdk.AttributePlanModifier {
return RequiresReplaceModifier{}
}

// RequiresReplaceModifier is an AttributePlanModifier that sets RequiresReplace
// on the attribute if the current time is past the stored timestamp.
//
// This custom modifier is necessary because the resource.RequiresReplaceIf
// function uses special logic for Computed attributes which is not applicable
// this use case.
type RequiresReplaceModifier struct{}

func (r RequiresReplaceModifier) Description(ctx context.Context) string {
return "value must be a string in RFC3339 format"
}

func (r RequiresReplaceModifier) MarkdownDescription(ctx context.Context) string {
return r.Description(ctx)
}

func (r RequiresReplaceModifier) Modify(ctx context.Context, req tfsdk.ModifyAttributePlanRequest, resp *tfsdk.ModifyAttributePlanResponse) {
if req.AttributeConfig == nil || req.AttributePlan == nil || req.AttributeState == nil {
// shouldn't happen, but let's not panic if it does
return
}

if req.State.Raw.IsNull() {
// if we're creating the resource, no need to delete and
// recreate it
return
}

if req.Plan.Raw.IsNull() {
// if we're deleting the resource, no need to delete and
// recreate it
return
}

var rotationRFC3339 types.String
diags := tfsdk.ValueAs(ctx, req.AttributeState, &rotationRFC3339)
if diags.HasError() {
resp.Diagnostics.Append(diags...)
return
}

rotationTimestamp, err := time.Parse(time.RFC3339, rotationRFC3339.Value)
if err != nil {
resp.Diagnostics.AddError(
"replaceIfOutdated plan modifier error",
"The rotation rfc3339 timestamp that was supplied could not be parsed as RFC3339.\n\n+"+
fmt.Sprintf("Original Error: %s", err),
)
return
}

now := time.Now().UTC()

resp.RequiresReplace = now.After(rotationTimestamp)
}
47 changes: 47 additions & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package provider

import (
"context"

"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/provider"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
)

func New() provider.Provider {
return &timeProvider{}
}

var (
_ provider.Provider = (*timeProvider)(nil)
_ provider.ProviderWithMetadata = (*timeProvider)(nil)
)

type timeProvider struct{}

func (p *timeProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) {
resp.TypeName = "time"
}

func (p *timeProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {

}

func (p *timeProvider) DataSources(ctx context.Context) []func() datasource.DataSource {
return nil
}

func (p *timeProvider) Resources(ctx context.Context) []func() resource.Resource {
return []func() resource.Resource{
NewTimeOffsetResource,
NewTimeRotatingResource,
NewTimeSleepResource,
NewTimeStaticResource,
}
}

func (p *timeProvider) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
return tfsdk.Schema{}, nil
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
package tftime
package provider

import (
"fmt"
"time"

"github.com/hashicorp/terraform-plugin-framework/providerserver"

"github.com/hashicorp/terraform-plugin-go/tfprotov5"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)

//nolint:unparam
var testAccProviderFactories = map[string]func() (*schema.Provider, error){
"time": func() (*schema.Provider, error) {
return Provider(), nil
},
func protoV5ProviderFactories() map[string]func() (tfprotov5.ProviderServer, error) {
return map[string]func() (tfprotov5.ProviderServer, error){
"time": providerserver.NewProtocol5WithError(New()),
}
}

func providerVersion080() map[string]resource.ExternalProvider {
return map[string]resource.ExternalProvider{
"time": {
VersionConstraint: "0.8.0",
Source: "hashicorp/time",
},
}
}

func testCheckAttributeValuesDiffer(i *string, j *string) resource.TestCheckFunc {
Expand All @@ -36,6 +47,7 @@ func testCheckAttributeValuesSame(i *string, j *string) resource.TestCheckFunc {
}
}

//nolint:unparam
func testExtractResourceAttr(resourceName string, attributeName string, attributeValue *string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[resourceName]
Expand Down
Loading

0 comments on commit ea34431

Please sign in to comment.