Skip to content

Commit

Permalink
update tfsdk helpers to use *diags (elastic#855)
Browse files Browse the repository at this point in the history
* update tfsdk helpers to use *diags

* cl

* all diags params get pointers

* all diags params get pointers

* amend changelog

* impl upgradeV0 for integration policy

* cl
  • Loading branch information
daemitus authored and kjwardy committed Oct 21, 2024
1 parent ce72b5f commit c536010
Show file tree
Hide file tree
Showing 13 changed files with 231 additions and 71 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- Support updating `elasticstack_elasticsearch_security_api_key` when supported by the backing cluster ([#843](https://github.com/elastic/terraform-provider-elasticstack/pull/843))
- Fix validation of `throttle`, and `interval` attributes in `elasticstack_kibana_alerting_rule` allowing all Elastic duration values ([#846](https://github.com/elastic/terraform-provider-elasticstack/pull/846))
- Fix boolean setting parsing for `elasticstack_elasticsearch_indices` data source. ([#842](https://github.com/elastic/terraform-provider-elasticstack/pull/842))
- Update all Fleet and utils/tfsdk instances of diagnostics parameters to pass by pointer instead of pass by value. Added upgrader for fleet_integration_policy v0 to handle empty string vars_json/streams_json. ([#855](https://github.com/elastic/terraform-provider-elasticstack/pull/855))

## [0.11.9] - 2024-10-14

Expand Down
2 changes: 1 addition & 1 deletion internal/fleet/enrollment_tokens/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type enrollmentTokenModel struct {
}

func (model *enrollmentTokensModel) populateFromAPI(ctx context.Context, data []fleetapi.EnrollmentApiKey) (diags diag.Diagnostics) {
model.Tokens = utils.SliceToListType(ctx, data, getTokenType(), path.Root("tokens"), diags, newEnrollmentTokenModel)
model.Tokens = utils.SliceToListType(ctx, data, getTokenType(), path.Root("tokens"), &diags, newEnrollmentTokenModel)
return
}

Expand Down
4 changes: 2 additions & 2 deletions internal/fleet/integration/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import (
)

func (r *integrationResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
r.create(ctx, req.Plan, &resp.State, resp.Diagnostics)
r.create(ctx, req.Plan, &resp.State, &resp.Diagnostics)
}

func (r integrationResource) create(ctx context.Context, plan tfsdk.Plan, state *tfsdk.State, respDiags diag.Diagnostics) {
func (r integrationResource) create(ctx context.Context, plan tfsdk.Plan, state *tfsdk.State, respDiags *diag.Diagnostics) {
var planModel integrationModel

diags := plan.Get(ctx, &planModel)
Expand Down
2 changes: 1 addition & 1 deletion internal/fleet/integration/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ import (
)

func (r *integrationResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
r.create(ctx, req.Plan, &resp.State, resp.Diagnostics)
r.create(ctx, req.Plan, &resp.State, &resp.Diagnostics)
}
19 changes: 10 additions & 9 deletions internal/fleet/integration_policy/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,14 @@ func (model *integrationPolicyModel) populateFromAPI(ctx context.Context, data *
model.Enabled = types.BoolPointerValue(data.Enabled)
model.IntegrationName = types.StringValue(data.Package.Name)
model.IntegrationVersion = types.StringValue(data.Package.Version)
model.VarsJson = utils.MapToNormalizedType(utils.Deref(data.Vars), path.Root("vars_json"), diags)
model.VarsJson = utils.MapToNormalizedType(utils.Deref(data.Vars), path.Root("vars_json"), &diags)

model.populateInputFromAPI(ctx, data.Inputs, diags)
model.populateInputFromAPI(ctx, data.Inputs, &diags)

return diags
}

func (model *integrationPolicyModel) populateInputFromAPI(ctx context.Context, inputs map[string]fleetapi.PackagePolicyInput, diags diag.Diagnostics) {
func (model *integrationPolicyModel) populateInputFromAPI(ctx context.Context, inputs map[string]fleetapi.PackagePolicyInput, diags *diag.Diagnostics) {
newInputs := utils.TransformMapToSlice(inputs, path.Root("input"), diags,
func(inputData fleetapi.PackagePolicyInput, meta utils.MapMeta) integrationPolicyInputModel {
return integrationPolicyInputModel{
Expand All @@ -68,12 +68,13 @@ func (model *integrationPolicyModel) populateInputFromAPI(ctx context.Context, i
}
})
if newInputs == nil {
model.Input = types.ListNull(getInputType())
model.Input = types.ListNull(getInputTypeV1())
} else {
oldInputs := utils.ListTypeAs[integrationPolicyInputModel](ctx, model.Input, path.Root("input"), diags)

sortInputs(newInputs, oldInputs)

inputList, d := types.ListValueFrom(ctx, getInputType(), newInputs)
inputList, d := types.ListValueFrom(ctx, getInputTypeV1(), newInputs)
diags.Append(d...)

model.Input = inputList
Expand All @@ -96,19 +97,19 @@ func (model integrationPolicyModel) toAPIModel(ctx context.Context, isUpdate boo
Version: model.IntegrationVersion.ValueString(),
},
PolicyId: model.AgentPolicyID.ValueString(),
Vars: utils.MapRef(utils.NormalizedTypeToMap[any](model.VarsJson, path.Root("vars_json"), diags)),
Vars: utils.MapRef(utils.NormalizedTypeToMap[any](model.VarsJson, path.Root("vars_json"), &diags)),
}

if isUpdate {
body.Id = model.ID.ValueStringPointer()
}

body.Inputs = utils.MapRef(utils.ListTypeToMap(ctx, model.Input, path.Root("input"), diags,
body.Inputs = utils.MapRef(utils.ListTypeToMap(ctx, model.Input, path.Root("input"), &diags,
func(inputModel integrationPolicyInputModel, meta utils.ListMeta) (string, fleetapi.PackagePolicyRequestInput) {
return inputModel.InputID.ValueString(), fleetapi.PackagePolicyRequestInput{
Enabled: inputModel.Enabled.ValueBoolPointer(),
Streams: utils.MapRef(utils.NormalizedTypeToMap[fleetapi.PackagePolicyRequestInputStream](inputModel.StreamsJson, meta.Path.AtName("streams_json"), diags)),
Vars: utils.MapRef(utils.NormalizedTypeToMap[any](inputModel.VarsJson, meta.Path.AtName("vars_json"), diags)),
Streams: utils.MapRef(utils.NormalizedTypeToMap[fleetapi.PackagePolicyRequestInputStream](inputModel.StreamsJson, meta.Path.AtName("streams_json"), &diags)),
Vars: utils.MapRef(utils.NormalizedTypeToMap[any](inputModel.VarsJson, meta.Path.AtName("vars_json"), &diags)),
}
}))

Expand Down
13 changes: 10 additions & 3 deletions internal/fleet/integration_policy/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import (
)

var (
_ resource.Resource = &integrationPolicyResource{}
_ resource.ResourceWithConfigure = &integrationPolicyResource{}
_ resource.ResourceWithImportState = &integrationPolicyResource{}
_ resource.Resource = &integrationPolicyResource{}
_ resource.ResourceWithConfigure = &integrationPolicyResource{}
_ resource.ResourceWithImportState = &integrationPolicyResource{}
_ resource.ResourceWithUpgradeState = &integrationPolicyResource{}
)

// NewResource is a helper function to simplify the provider implementation.
Expand All @@ -37,3 +38,9 @@ func (r *integrationPolicyResource) Metadata(ctx context.Context, req resource.M
func (r *integrationPolicyResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
resource.ImportStatePassthroughID(ctx, path.Root("policy_id"), req, resp)
}

func (r *integrationPolicyResource) UpgradeState(context.Context) map[int64]resource.StateUpgrader {
return map[int64]resource.StateUpgrader{
0: {PriorSchema: getSchemaV0(), StateUpgrader: upgradeV0},
}
}
9 changes: 5 additions & 4 deletions internal/fleet/integration_policy/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ import (
)

func (r *integrationPolicyResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = getSchema()
resp.Schema = getSchemaV1()
}

func getSchema() schema.Schema {
func getSchemaV1() schema.Schema {
return schema.Schema{
Version: 1,
Description: "Creates a new Fleet Integration Policy. See https://www.elastic.co/guide/en/fleet/current/add-integration-to-policy.html",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Expand Down Expand Up @@ -114,6 +115,6 @@ func getSchema() schema.Schema {
}
}

func getInputType() attr.Type {
return getSchema().Blocks["input"].Type().(attr.TypeWithElementType).ElementType()
func getInputTypeV1() attr.Type {
return getSchemaV1().Blocks["input"].Type().(attr.TypeWithElementType).ElementType()
}
114 changes: 114 additions & 0 deletions internal/fleet/integration_policy/upgrade.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package integration_policy

import (
"context"

"github.com/elastic/terraform-provider-elasticstack/internal/utils"
"github.com/hashicorp/terraform-plugin-framework/attr"
"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/booldefault"
"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"
)

func getSchemaV0() *schema.Schema {
return &schema.Schema{
Version: 0,
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}},
"policy_id": schema.StringAttribute{Computed: true, Optional: true, PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace(), stringplanmodifier.UseStateForUnknown()}},
"name": schema.StringAttribute{Required: true},
"namespace": schema.StringAttribute{Required: true},
"agent_policy_id": schema.StringAttribute{Required: true},
"description": schema.StringAttribute{Optional: true},
"enabled": schema.BoolAttribute{Computed: true, Optional: true, Default: booldefault.StaticBool(true)},
"force": schema.BoolAttribute{Optional: true},
"integration_name": schema.StringAttribute{Required: true},
"integration_version": schema.StringAttribute{Required: true},
"vars_json": schema.StringAttribute{Computed: true, Optional: true, Sensitive: true},
},
Blocks: map[string]schema.Block{
"input": schema.ListNestedBlock{
NestedObject: schema.NestedBlockObject{
Attributes: map[string]schema.Attribute{
"input_id": schema.StringAttribute{Required: true},
"enabled": schema.BoolAttribute{Computed: true, Optional: true, Default: booldefault.StaticBool(true)},
"streams_json": schema.StringAttribute{Computed: true, Optional: true, Sensitive: true},
"vars_json": schema.StringAttribute{Computed: true, Optional: true, Sensitive: true},
},
},
},
},
}
}

func getInputTypeV0() attr.Type {
return getSchemaV0().Blocks["input"].Type().(attr.TypeWithElementType).ElementType()
}

type integrationPolicyModelV0 struct {
ID types.String `tfsdk:"id"`
PolicyID types.String `tfsdk:"policy_id"`
Name types.String `tfsdk:"name"`
Namespace types.String `tfsdk:"namespace"`
AgentPolicyID types.String `tfsdk:"agent_policy_id"`
Description types.String `tfsdk:"description"`
Enabled types.Bool `tfsdk:"enabled"`
Force types.Bool `tfsdk:"force"`
IntegrationName types.String `tfsdk:"integration_name"`
IntegrationVersion types.String `tfsdk:"integration_version"`
Input types.List `tfsdk:"input"` //> integrationPolicyInputModelV0
VarsJson types.String `tfsdk:"vars_json"`
}

type integrationPolicyInputModelV0 struct {
InputID types.String `tfsdk:"input_id"`
Enabled types.Bool `tfsdk:"enabled"`
StreamsJson types.String `tfsdk:"streams_json"`
VarsJson types.String `tfsdk:"vars_json"`
}

// The schema between V0 and V1 is mostly the same, however vars_json and
// streams_json saved "" values to the state when null values were in the
// config. jsontypes.Normalized correctly states this is invalid JSON.
func upgradeV0(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) {
var stateModel integrationPolicyModelV0

diags := req.State.Get(ctx, &stateModel)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

if varsJSON := stateModel.VarsJson.ValueStringPointer(); varsJSON != nil {
if *varsJSON == "" {
stateModel.VarsJson = types.StringNull()
}
}

inputs := utils.ListTypeAs[integrationPolicyInputModelV0](ctx, stateModel.Input, path.Root("input"), &resp.Diagnostics)
for index, input := range inputs {
if varsJSON := input.VarsJson.ValueStringPointer(); varsJSON != nil {
if *varsJSON == "" {
input.VarsJson = types.StringNull()
}
}
if streamsJSON := input.StreamsJson.ValueStringPointer(); streamsJSON != nil {
if *streamsJSON == "" {
input.StreamsJson = types.StringNull()
}
}
inputs[index] = input
}

stateModel.Input = utils.ListValueFrom(ctx, inputs, getInputTypeV0(), path.Root("input"), &resp.Diagnostics)
if resp.Diagnostics.HasError() {
return
}

diags = resp.State.Set(ctx, stateModel)
resp.Diagnostics.Append(diags...)
}
Loading

0 comments on commit c536010

Please sign in to comment.