From c46ce1604d5c1ba6408a27a9653ce03ebf19a38a Mon Sep 17 00:00:00 2001 From: Andrew Reed <andrew@smallstep.com> Date: Fri, 20 Dec 2024 13:22:08 -0600 Subject: [PATCH] Add device resource tests --- internal/apiclient/v20250101/api.gen.go | 342 +++++++++++----------- internal/provider/device/model.go | 41 ++- internal/provider/device/resource.go | 178 ++++++----- internal/provider/device/resource_test.go | 88 ++++++ internal/provider/provider.go | 2 +- internal/provider/utils/optional.go | 9 + internal/provider/utils/testutils.go | 2 +- internal/provider/utils/utils.go | 2 +- 8 files changed, 410 insertions(+), 254 deletions(-) create mode 100644 internal/provider/device/resource_test.go diff --git a/internal/apiclient/v20250101/api.gen.go b/internal/apiclient/v20250101/api.gen.go index da38b1e..3844336 100644 --- a/internal/apiclient/v20250101/api.gen.go +++ b/internal/apiclient/v20250101/api.gen.go @@ -391,6 +391,8 @@ type Device struct { // Tags A set of tags that can be used to group devices. Tags *DeviceTags `json:"tags,omitempty"` + + // User The user that a device is assigned to. A device cannot be approved for high-assurance certificates until it has an assigned user. User *DeviceUser `json:"user,omitempty"` } @@ -502,6 +504,8 @@ type DeviceRequest struct { // Tags A set of tags that can be used to group devices. Tags *DeviceTags `json:"tags,omitempty"` + + // User The user that a device is assigned to. A device cannot be approved for high-assurance certificates until it has an assigned user. User *DeviceUser `json:"user,omitempty"` } @@ -513,13 +517,14 @@ type DeviceSerial = string // DeviceTags A set of tags that can be used to group devices. type DeviceTags = []string -// DeviceUser defines model for deviceUser. +// DeviceUser The user that a device is assigned to. A device cannot be approved for high-assurance certificates until it has an assigned user. type DeviceUser struct { // DisplayName Full name of the user the device is assigned to. Synced from team's identity provider. Read only. DisplayName *string `json:"displayName,omitempty"` // Email Email of the user the device is assigned to. - // Setting this value will mask any value synced from an MDM. + // This field may be populated with a value derived from data synced from your team's MDMs. + // Setting this value explicitly will mask any MDM-derived value. Email string `json:"email"` } @@ -6035,172 +6040,173 @@ var swaggerSpec = []string{ "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", - "AAD//5PVeIeXDgEA", + "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", + "Z/T1fHrSfxvAzw8WmgSv+4N+q3k6Oz4eWa/OzE/z3dm0eQm/Hn51Tw7p9Hh+Ync+Rxe1+8b5LnrAjtmn", + "k+DOI7tkdDxxdiedy05z7zSYjGefLuze4NP7Qf0tPN1FNr4mb8Z48NC7W/YvWnfL6y/vv0a0eTo1dy2r", + "sesfzacBGd5NnHa4a9nWW2KZTmt8v3s/6+P3d++++N3BGTwZTMfn++6rzuzz5y/NAQrfvz+p9dve6VF0", + "ufhkudGi6jVOyN1X6vbvzk/33r46vD8dNU8XcQn1lt1Bk8Z+zbTs2tRsQVg3J3C/bXYsOG1Be7o3hZaR", + "eIWhMzMOjMFIWEat4J4pBab4646P530ZLJr9F4ftLxD69jj4cvfJ879evei/PYJn48MX+8Np9GbaCjx8", + "xvqES253FWm+THBjpqY8GAfGu65/FnVQ96TrTk/P8SwcNUMn3HvRdK3WwzvLikjXf3XUwfc9thY2zPGr", + "2fDwvXlh1cLu2/HnzjsX7c9PX7/qBX4zmnp3rlsPrS/7F1FNOS+NsyUo9Uyz84h7tuRN/IOByOyvjOxg", + "50iOeZtmrZVn3uRe98EodI/vV3x0Hi7HENm263vtZr1hTnetmtlqNXbNPThtmHa9Ntltt/ZsC06Nwj1I", + "vKdeyzzcUq7z82OXLZDPaDU7k1q7MTF36+2a2drrWOakvW+ZNXvPqiPLRrt8xnTgt+38uIF4gu1D2csJ", + "/Odqtaqt8c9/VM5ldtoz4NSnmjZ5YSl5IEm6P8XippNma2/aMKftac1s7e83zf3WXsNs2y3U2a3BerOG", + "ss/dyBeo1NUpLx9lnqbnGgsnFM3zhB/it+6KT9X9IJ6ShUqvB1tno9NptHYnNbMxaSCz1aztm5NJu2W2", + "UGu6O5027EnTzqxzfHm24okwdckSncqaM+pZ+frlk3/iBcASuNvN6d7eXgOadtNumi0bIRM2GhOzCVET", + "dvZ3a9NJbn9+GsxJrNgHI6lYXmNwaiIOk/o+2ghCZT21Vm3abLX3Tbtp7ZmtSYvvQ8dsWO3dSa1Zr9ud", + "ppGL+jLabo1mF1lWEprpwAz4HwFxv2NPEGzWzeluvW62GnbN3G+1kdmcTtotVK/V6x24HsRiXJ4aMvjB", + "8O+wyVAqKxtko/NUjJuypNNfNrpHDrszasOJK8b+Xme33Wo26pt2SUUKuzT8KNp2p3DSsfcts21Nkdlq", + "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", + "+PHx/wUAAP//it8lPwsQAQA=", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/internal/provider/device/model.go b/internal/provider/device/model.go index 9655181..9b1ad83 100644 --- a/internal/provider/device/model.go +++ b/internal/provider/device/model.go @@ -115,16 +115,24 @@ func fromAPI(ctx context.Context, device *v20250101.Device, state utils.Attribut metadata, d := types.MapValue(types.StringType, meta) diags.Append(d...) model.Metadata = metadata + } else { + model.Metadata = types.MapNull(types.StringType) } if device.ApprovedAt != nil { model.ApprovedAt = types.StringValue((*device.ApprovedAt).Format(time.RFC3339)) + } else { + model.ApprovedAt = types.StringNull() } if device.EnrolledAt != nil { model.EnrolledAt = types.StringValue((*device.EnrolledAt).Format(time.RFC3339)) + } else { + model.EnrolledAt = types.StringNull() } if device.LastSeen != nil { model.LastSeen = types.StringValue((*device.LastSeen).Format(time.RFC3339)) + } else { + model.LastSeen = types.StringNull() } return model, diags @@ -136,41 +144,44 @@ func toAPI(ctx context.Context, m *Model) (*v20250101.DeviceRequest, diag.Diagno d := &v20250101.DeviceRequest{ PermanentIdentifier: m.PermanentIdentifier.ValueString(), - DisplayId: m.DisplayID.ValueStringPointer(), - DisplayName: m.DisplayName.ValueStringPointer(), - Serial: m.Serial.ValueStringPointer(), } - if os := m.OS.ValueStringPointer(); os != nil { - d.Os = utils.Ref(v20250101.DeviceRequestOs(m.OS.ValueString())) + if !m.DisplayID.IsNull() && !m.DisplayID.IsUnknown() { + d.DisplayId = m.DisplayID.ValueStringPointer() } - if ownership := m.Ownership.ValueStringPointer(); ownership != nil { - d.Ownership = utils.Ref(v20250101.DeviceRequestOwnership(m.Ownership.ValueString())) + if !m.DisplayName.IsNull() && !m.DisplayName.IsUnknown() { + d.DisplayName = m.DisplayName.ValueStringPointer() } - - if !m.Metadata.IsNull() { + if !m.Serial.IsNull() && !m.Serial.IsUnknown() { + d.Serial = m.Serial.ValueStringPointer() + } + if !m.OS.IsNull() && !m.OS.IsUnknown() { + d.Os = utils.Ref(v20250101.DeviceOS(m.OS.ValueString())) + } + if !m.Ownership.IsNull() && !m.Ownership.IsUnknown() { + d.Ownership = utils.Ref(v20250101.DeviceOwnership(m.Ownership.ValueString())) + } + if !m.Metadata.IsNull() && !m.Ownership.IsUnknown() { meta := map[string]types.String{} diag := m.Metadata.ElementsAs(ctx, &meta, false) diags.Append(diag...) - var metadata []v20250101.DeviceMetadata + var metadata []v20250101.DeviceMetadataItem for k, v := range meta { - metadata = append(metadata, v20250101.DeviceMetadata{ + metadata = append(metadata, v20250101.DeviceMetadataItem{ Key: k, Value: v.ValueString(), }) } d.Metadata = &metadata } - - if !m.Tags.IsNull() { + if !m.Tags.IsNull() && !m.Tags.IsUnknown() { var tags []string diag := m.Tags.ElementsAs(ctx, &tags, false) diags.Append(diag...) d.Tags = &tags } - - if !m.User.IsNull() { + if !m.User.IsNull() && !m.User.IsUnknown() { user := &UserModel{} diag := m.User.As(ctx, &user, basetypes.ObjectAsOptions{ UnhandledUnknownAsEmpty: true, diff --git a/internal/provider/device/resource.go b/internal/provider/device/resource.go index 6f129ce..e42b287 100644 --- a/internal/provider/device/resource.go +++ b/internal/provider/device/resource.go @@ -9,6 +9,9 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" v20250101 "github.com/smallstep/terraform-provider-smallstep/internal/apiclient/v20250101" "github.com/smallstep/terraform-provider-smallstep/internal/provider/utils" ) @@ -49,9 +52,12 @@ func (r *Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp "id": schema.StringAttribute{ MarkdownDescription: props["id"], Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, }, "permanent_identifier": schema.StringAttribute{ - MarkdownDescription: props["permanent_identifier"], + MarkdownDescription: props["permanentIdentifier"], Required: true, }, "serial": schema.StringAttribute{ @@ -83,11 +89,13 @@ func (r *Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp MarkdownDescription: props["metadata"], Optional: true, Computed: true, + ElementType: types.StringType, }, "tags": schema.SetAttribute{ MarkdownDescription: props["tags"], Optional: true, Computed: true, + ElementType: types.StringType, }, "user": schema.SingleNestedAttribute{ MarkdownDescription: deviceUser, @@ -105,6 +113,26 @@ func (r *Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp }, }, }, + "connected": schema.BoolAttribute{ + MarkdownDescription: props["connected"], + Computed: true, + }, + "high_assurance": schema.BoolAttribute{ + MarkdownDescription: props["highAssurance"], + Computed: true, + }, + "enrolled_at": schema.StringAttribute{ + MarkdownDescription: props["enrolledAt"], + Computed: true, + }, + "approved_at": schema.StringAttribute{ + MarkdownDescription: props["approvedAt"], + Computed: true, + }, + "last_seen": schema.StringAttribute{ + MarkdownDescription: props["lastSeen"], + Computed: true, + }, }, } } @@ -141,11 +169,20 @@ func (r *Resource) Read(ctx context.Context, req resource.ReadRequest, resp *res return } - httpResp, err := r.client.GetAccount(ctx, state.ID.ValueString(), &v20250101.GetAccountParams{}) + deviceID := state.ID.ValueString() + if deviceID == "" { + resp.Diagnostics.AddError( + "Invalid Delete Device Request", + "Device ID is required", + ) + return + } + + httpResp, err := r.client.GetDevice(ctx, deviceID, &v20250101.GetDeviceParams{}) if err != nil { resp.Diagnostics.AddError( "Smallstep API Client Error", - fmt.Sprintf("Failed to read account %q: %v", state.ID.ValueString(), err), + fmt.Sprintf("Failed to read device %q: %v", state.ID.ValueString(), err), ) return } @@ -159,21 +196,21 @@ func (r *Resource) Read(ctx context.Context, req resource.ReadRequest, resp *res reqID := httpResp.Header.Get("X-Request-Id") resp.Diagnostics.AddError( "Smallstep API Response Error", - fmt.Sprintf("Request %q received status %d reading account %s: %s", reqID, httpResp.StatusCode, state.ID.String(), utils.APIErrorMsg(httpResp.Body)), + fmt.Sprintf("Request %q received status %d reading device %s: %s", reqID, httpResp.StatusCode, deviceID, utils.APIErrorMsg(httpResp.Body)), ) return } - account := &v20250101.Account{} - if err := json.NewDecoder(httpResp.Body).Decode(account); err != nil { + device := &v20250101.Device{} + if err := json.NewDecoder(httpResp.Body).Decode(device); err != nil { resp.Diagnostics.AddError( "Smallstep API Client Error", - fmt.Sprintf("Failed to unmarshal account %s: %v", state.ID.String(), err), + fmt.Sprintf("Failed to unmarshal device %s: %v", deviceID, err), ) return } - remote, d := fromAPI(ctx, account, req.State) + remote, d := fromAPI(ctx, device, req.State) resp.Diagnostics.Append(d...) if resp.Diagnostics.HasError() { return @@ -195,11 +232,11 @@ func (a *Resource) Create(ctx context.Context, req resource.CreateRequest, resp return } - httpResp, err := a.client.PostAccounts(ctx, &v20250101.PostAccountsParams{}, *reqBody) + httpResp, err := a.client.PostDevices(ctx, &v20250101.PostDevicesParams{}, *reqBody) if err != nil { resp.Diagnostics.AddError( "Smallstep API Client Error", - fmt.Sprintf("Failed to create account: %v", err), + fmt.Sprintf("Failed to create device: %v", err), ) return } @@ -209,21 +246,21 @@ func (a *Resource) Create(ctx context.Context, req resource.CreateRequest, resp reqID := httpResp.Header.Get("X-Request-Id") resp.Diagnostics.AddError( "Smallstep API Response Error", - fmt.Sprintf("Request %q received status %d creating account: %s", reqID, httpResp.StatusCode, utils.APIErrorMsg(httpResp.Body)), + fmt.Sprintf("Request %q received status %d creating device: %s", reqID, httpResp.StatusCode, utils.APIErrorMsg(httpResp.Body)), ) return } - account := &v20250101.Account{} - if err := json.NewDecoder(httpResp.Body).Decode(account); err != nil { + device := &v20250101.Device{} + if err := json.NewDecoder(httpResp.Body).Decode(device); err != nil { resp.Diagnostics.AddError( "Smallstep API Client Error", - fmt.Sprintf("Failed to unmarshal account: %v", err), + fmt.Sprintf("Failed to unmarshal device: %v", err), ) return } - model, diags := fromAPI(ctx, account, req.Plan) + model, diags := fromAPI(ctx, device, req.Plan) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return @@ -234,55 +271,60 @@ func (a *Resource) Create(ctx context.Context, req resource.CreateRequest, resp } func (r *Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - plan := &Model{} - diags := req.Plan.Get(ctx, plan) - if diags.HasError() { + resp.Diagnostics.AddError( + "Update not supported", + "All changes require replacement", + ) + /* + plan := &Model{} + diags := req.Plan.Get(ctx, plan) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + var deviceID string + req.State.GetAttribute(ctx, path.Root("id"), &deviceID) + + patch, diags := toAPI(ctx, plan) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + + httpResp, err := r.client.PatchDevice(ctx, deviceID, &v20250101.PatchDeviceParams{}, *patch) + if err != nil { + resp.Diagnostics.AddError( + "Smallstep API Client Error", + err.Error(), + ) + return + } + defer httpResp.Body.Close() + + if httpResp.StatusCode != http.StatusOK { + reqID := httpResp.Header.Get("X-Request-Id") + resp.Diagnostics.AddError( + "Smallstep API Response Error", + fmt.Sprintf("Request %q received status %d updating device: %s", reqID, httpResp.StatusCode, utils.APIErrorMsg(httpResp.Body)), + ) + return + } + + device := &v20250101.Device{} + if err := json.NewDecoder(httpResp.Body).Decode(device); err != nil { + resp.Diagnostics.AddError( + "Smallstep API Client Error", + fmt.Sprintf("Failed to parse device update response: %v", err), + ) + return + } + + model, diags := fromAPI(ctx, device, req.Plan) resp.Diagnostics.Append(diags...) - return - } - var accountID string - req.State.GetAttribute(ctx, path.Root("id"), &accountID) - account, diags := toAPI(ctx, plan) - if diags.HasError() { + diags = resp.State.Set(ctx, model) resp.Diagnostics.Append(diags...) - return - } - account.Id = &accountID - - httpResp, err := r.client.PutAccount(ctx, accountID, &v20250101.PutAccountParams{}, *account) - if err != nil { - resp.Diagnostics.AddError( - "Smallstep API Client Error", - err.Error(), - ) - return - } - defer httpResp.Body.Close() - - if httpResp.StatusCode != http.StatusOK { - reqID := httpResp.Header.Get("X-Request-Id") - resp.Diagnostics.AddError( - "Smallstep API Response Error", - fmt.Sprintf("Request %q received status %d updating account: %s", reqID, httpResp.StatusCode, utils.APIErrorMsg(httpResp.Body)), - ) - return - } - - account = &v20250101.Account{} - if err := json.NewDecoder(httpResp.Body).Decode(account); err != nil { - resp.Diagnostics.AddError( - "Smallstep API Client Error", - fmt.Sprintf("Failed to parse account update response: %v", err), - ) - return - } - - model, diags := fromAPI(ctx, account, req.Plan) - resp.Diagnostics.Append(diags...) - - diags = resp.State.Set(ctx, model) - resp.Diagnostics.Append(diags...) + */ } func (r *Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { @@ -293,20 +335,20 @@ func (r *Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp return } - accountID := state.ID.ValueString() - if accountID == "" { + deviceID := state.ID.ValueString() + if deviceID == "" { resp.Diagnostics.AddError( - "Invalid Delete Account Request", - "Account ID is required", + "Invalid Delete Device Request", + "Device ID is required", ) return } - httpResp, err := r.client.DeleteAccount(ctx, accountID, &v20250101.DeleteAccountParams{}) + httpResp, err := r.client.DeleteDevice(ctx, deviceID, &v20250101.DeleteDeviceParams{}) if err != nil { resp.Diagnostics.AddError( "Smallstep API Client Error", - fmt.Sprintf("Failed to delete account: %v", err), + fmt.Sprintf("Failed to delete device: %v", err), ) return } @@ -316,7 +358,7 @@ func (r *Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp reqID := httpResp.Header.Get("X-Request-Id") resp.Diagnostics.AddError( "Smallstep API Response Error", - fmt.Sprintf("Request %q received status %d updating account: %s", reqID, httpResp.StatusCode, utils.APIErrorMsg(httpResp.Body)), + fmt.Sprintf("Request %q received status %d updating device: %s", reqID, httpResp.StatusCode, utils.APIErrorMsg(httpResp.Body)), ) return } diff --git a/internal/provider/device/resource_test.go b/internal/provider/device/resource_test.go new file mode 100644 index 0000000..4b11905 --- /dev/null +++ b/internal/provider/device/resource_test.go @@ -0,0 +1,88 @@ +package device + +import ( + "fmt" + "regexp" + "testing" + + "github.com/google/uuid" + "github.com/hashicorp/terraform-plugin-framework/providerserver" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + helper "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/smallstep/terraform-provider-smallstep/internal/testprovider" +) + +var provider = &testprovider.SmallstepTestProvider{ + ResourceFactories: []func() resource.Resource{ + NewResource, + }, + /* + DataSourceFactories: []func() datasource.DataSource{ + NewDataSource, + }, + */ +} + +var providerFactories = map[string]func() (tfprotov6.ProviderServer, error){ + "smallstep": providerserver.NewProtocol6WithError(provider), +} + +func TestAccAuthorityResource(t *testing.T) { + t.Parallel() + + permanentID := uuid.NewString() + + emptyConfig := fmt.Sprintf(` +resource "smallstep_device" "laptop1" { + permanent_identifier = %q +}`, permanentID) + + helper.Test(t, helper.TestCase{ + ProtoV6ProviderFactories: providerFactories, + Steps: []helper.TestStep{ + { + Config: emptyConfig, + Check: helper.ComposeAggregateTestCheckFunc( + helper.TestMatchResourceAttr("smallstep_device.laptop1", "id", regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`)), + helper.TestCheckResourceAttr("smallstep_device.laptop1", "permanent_identifier", permanentID), + ), + }, + { + ResourceName: "smallstep_device.laptop1", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) + + permanentID = uuid.NewString() + fullConfig := fmt.Sprintf(` +resource "smallstep_device" "laptop1" { + permanent_identifier = %q + display_id = "9 9" + serial = "678" + tags = ["ubuntu"] +}`, permanentID) + + helper.Test(t, helper.TestCase{ + ProtoV6ProviderFactories: providerFactories, + Steps: []helper.TestStep{ + { + Config: fullConfig, + Check: helper.ComposeAggregateTestCheckFunc( + helper.TestMatchResourceAttr("smallstep_device.laptop1", "id", regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`)), + helper.TestCheckResourceAttr("smallstep_device.laptop1", "permanent_identifier", permanentID), + helper.TestCheckResourceAttr("smallstep_device.laptop1", "display_id", "9 9"), + helper.TestCheckResourceAttr("smallstep_device.laptop1", "serial", "678"), + helper.TestCheckResourceAttr("smallstep_device.laptop1", "tags.0", "ubuntu"), + ), + }, + { + ResourceName: "smallstep_device.laptop1", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 30a7e59..f19f6f3 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -149,7 +149,7 @@ func (p *SmallstepProvider) Configure(ctx context.Context, req provider.Configur } client, err := v20250101.NewClient(server, v20250101.WithRequestEditorFn(v20250101.RequestEditorFn(func(ctx context.Context, r *http.Request) error { - r.Header.Set("X-Smallstep-Api-Version", "2023-11-01") + r.Header.Set("X-Smallstep-Api-Version", "2025-01-01") r.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) return nil }))) diff --git a/internal/provider/utils/optional.go b/internal/provider/utils/optional.go index 6cd0b44..8a79117 100644 --- a/internal/provider/utils/optional.go +++ b/internal/provider/utils/optional.go @@ -23,6 +23,9 @@ func ToOptionalSet(ctx context.Context, remote *[]string, priorState AttributeGe if diags.HasError() { return types.Set{}, diags } + if setFromState.IsUnknown() { + return types.SetNull(types.StringType), diags + } if setFromState.IsNull() || len(setFromState.Elements()) == 0 { return setFromState, diags } @@ -69,6 +72,9 @@ func ToOptionalString[S ~string](ctx context.Context, remote *S, priorState Attr if diags.HasError() { return types.String{}, diags } + if stringFromState.IsUnknown() { + return types.StringNull(), diags + } if stringFromState.IsNull() || stringFromState.ValueString() == "" { return stringFromState, diags } @@ -88,6 +94,9 @@ func ToOptionalBool(ctx context.Context, remote *bool, priorState AttributeGette if diags.HasError() { return types.Bool{}, diags } + if boolFromState.IsUnknown() { + return types.BoolNull(), diags + } if boolFromState.IsNull() || boolFromState.ValueBool() == false { return boolFromState, diags } diff --git a/internal/provider/utils/testutils.go b/internal/provider/utils/testutils.go index 8b4c035..0b39783 100644 --- a/internal/provider/utils/testutils.go +++ b/internal/provider/utils/testutils.go @@ -34,7 +34,7 @@ func SmallstepAPIClientFromEnv() (*v20250101.Client, error) { } client, err := v20250101.NewClient(server, v20250101.WithRequestEditorFn(v20250101.RequestEditorFn(func(ctx context.Context, r *http.Request) error { - r.Header.Set("X-Smallstep-Api-Version", "2023-11-01") + r.Header.Set("X-Smallstep-Api-Version", "2025-01-01") r.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) return nil }))) diff --git a/internal/provider/utils/utils.go b/internal/provider/utils/utils.go index 5963fd7..1f76f57 100644 --- a/internal/provider/utils/utils.go +++ b/internal/provider/utils/utils.go @@ -80,7 +80,7 @@ func Describe(component string) (string, map[string]string, error) { if s == nil || s.Value == nil { continue } - if len(s.Value.Properties) > 0 { + if props == nil { props = s.Value.Properties continue } -- GitLab