Skip to content

Commit

Permalink
Db/add remaining state upgraders (#404)
Browse files Browse the repository at this point in the history
* WIP: add StateUpgraders for manual check and infra resources

* fix StateUpgraders for infra and manual check resources

* drop unused code
  • Loading branch information
davidbloss authored Jul 15, 2024
1 parent ae857bc commit 008f156
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 10 deletions.
4 changes: 4 additions & 0 deletions .changes/unreleased/Bugfix-20240715-143237.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
kind: Bugfix
body: fix Terraform state upgrades for previous provider versions on infrastructure
and manual check resources
time: 2024-07-15T14:32:37.017279-05:00
13 changes: 3 additions & 10 deletions opslevel/datasource_opslevel_webhook_actions_all.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import (
"context"
"fmt"

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

"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-log/tflog"
Expand All @@ -26,13 +24,12 @@ type webhookActionDataSourcesAllModel struct {
WebhookActions []webhookActionDataSourceModel `tfsdk:"webhook_actions"`
}

func newWebhookActionDataSourcesAllModel(ctx context.Context, webhookActions []opslevel.CustomActionsExternalAction) (webhookActionDataSourcesAllModel, diag.Diagnostics) {
var diags diag.Diagnostics
func newWebhookActionDataSourcesAllModel(webhookActions []opslevel.CustomActionsExternalAction) webhookActionDataSourcesAllModel {
webhookActionModels := make([]webhookActionDataSourceModel, 0)
for _, webhookAction := range webhookActions {
webhookActionModels = append(webhookActionModels, newWebhookActionDataSourceModel(webhookAction))
}
return webhookActionDataSourcesAllModel{WebhookActions: webhookActionModels}, diags
return webhookActionDataSourcesAllModel{WebhookActions: webhookActionModels}
}

func (d *WebhookActionDataSourcesAll) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
Expand Down Expand Up @@ -69,11 +66,7 @@ func (d *WebhookActionDataSourcesAll) Read(ctx context.Context, req datasource.R
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to list webhookActions, got error: %s", err))
return
}
stateModel, diags := newWebhookActionDataSourcesAllModel(ctx, webhookActions.Nodes)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
stateModel = newWebhookActionDataSourcesAllModel(webhookActions.Nodes)

tflog.Trace(ctx, "listed all OpsLevel WebhookAction data sources")
resp.Diagnostics.Append(resp.State.Set(ctx, &stateModel)...)
Expand Down
81 changes: 81 additions & 0 deletions opslevel/resource_opslevel_check_manual.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import (
"strings"

"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"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/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/opslevel/opslevel-go/v2024"
"github.com/relvacode/iso8601"
Expand All @@ -36,6 +38,12 @@ type CheckUpdateFrequency struct {
Value types.Int64 `tfsdk:"value"`
}

var updateFrequencyTypeV0 = map[string]attr.Type{
"starting_data": types.StringType,
"time_value": types.StringType,
"value": types.StringType,
}

type CheckManualResourceModel struct {
Category types.String `tfsdk:"category"`
Description types.String `tfsdk:"description"`
Expand Down Expand Up @@ -92,6 +100,7 @@ func (r *CheckManualResource) Metadata(ctx context.Context, req resource.Metadat

func (r *CheckManualResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Version: 1,
// This description is used by the documentation generator and the language server.
MarkdownDescription: "Check Manual Resource",

Expand Down Expand Up @@ -128,6 +137,78 @@ func (r *CheckManualResource) Schema(ctx context.Context, req resource.SchemaReq
}
}

func (r *CheckManualResource) UpgradeState(ctx context.Context) map[int64]resource.StateUpgrader {
return map[int64]resource.StateUpgrader{
// State upgrade implementation from 0 (prior state version) to 1 (Schema.Version)
0: {
PriorSchema: &schema.Schema{
Description: "Check Repository File Resource",
Attributes: getCheckBaseSchemaV0(map[string]schema.Attribute{
"id": schema.StringAttribute{
Description: "The ID of this resource.",
Computed: true,
},
"update_requires_comment": schema.BoolAttribute{
Description: "Whether the check requires a comment or not.",
Optional: true,
},
}),
Blocks: map[string]schema.Block{
"update_frequency": schema.ListNestedBlock{
NestedObject: schema.NestedBlockObject{
Attributes: map[string]schema.Attribute{
"starting_data": schema.StringAttribute{
Description: "The date that the check will start to evaluate.",
Required: true,
},
"time_scale": schema.StringAttribute{
Description: "The time scale type for the frequency.",
Required: true,
},
"value": schema.Int64Attribute{
Description: "The value to be used together with the frequency time_scale.",
Required: true,
},
},
},
},
},
},
StateUpgrader: func(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) {
// var diags diag.Diagnostics
upgradedStateModel := CheckManualResourceModel{}
updateFrequencyList := types.ListNull(types.ObjectType{AttrTypes: updateFrequencyTypeV0})

// base check attributes
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("category"), &upgradedStateModel.Category)...)
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("enable_on"), &upgradedStateModel.EnableOn)...)
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("enabled"), &upgradedStateModel.Enabled)...)
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("filter"), &upgradedStateModel.Filter)...)
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("id"), &upgradedStateModel.Id)...)
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("level"), &upgradedStateModel.Level)...)
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("name"), &upgradedStateModel.Name)...)
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("notes"), &upgradedStateModel.Notes)...)
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("owner"), &upgradedStateModel.Owner)...)

// repository file specific attributes
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("update_requires_comment"), &upgradedStateModel.UpdateRequiresComment)...)
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("update_frequency"), &updateFrequencyList)...)
if len(updateFrequencyList.Elements()) == 1 {
updateFrequency := updateFrequencyList.Elements()[0].(basetypes.ObjectValue)
updateFrequencyAttrs := updateFrequency.Attributes()
upgradedStateModel.UpdateFrequency = &CheckUpdateFrequency{
StartingDate: updateFrequencyAttrs["starting_data"].(basetypes.StringValue),
TimeScale: updateFrequencyAttrs["time_scale"].(basetypes.StringValue),
Value: updateFrequencyAttrs["value"].(basetypes.Int64Value),
}
}

resp.Diagnostics.Append(resp.State.Set(ctx, upgradedStateModel)...)
},
},
}
}

func (r *CheckManualResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var planModel CheckManualResourceModel

Expand Down
99 changes: 99 additions & 0 deletions opslevel/resource_opslevel_infra.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import (
"context"
"fmt"

"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/types/basetypes"

"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
Expand Down Expand Up @@ -37,6 +39,13 @@ type InfraProviderData struct {
Url types.String `tfsdk:"url"`
}

var infraProviderDataType = map[string]attr.Type{
"account": types.StringType,
"name": types.StringType,
"type": types.StringType,
"url": types.StringType,
}

func newInfraProviderData(infrastructure opslevel.InfrastructureResource) *InfraProviderData {
return &InfraProviderData{
Account: RequiredStringValue(infrastructure.ProviderData.AccountName),
Expand Down Expand Up @@ -85,6 +94,7 @@ func (r *InfrastructureResource) Metadata(ctx context.Context, req resource.Meta

func (r *InfrastructureResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Version: 1,
// This description is used by the documentation generator and the language server.
MarkdownDescription: "Infrastructure Resource",

Expand Down Expand Up @@ -144,6 +154,95 @@ func (r *InfrastructureResource) Schema(ctx context.Context, req resource.Schema
}
}

func (r *InfrastructureResource) UpgradeState(ctx context.Context) map[int64]resource.StateUpgrader {
return map[int64]resource.StateUpgrader{
// State upgrade implementation from 0 (prior state version) to 1 (Schema.Version)
0: {
PriorSchema: &schema.Schema{
Description: "Infrastructure Resource",
Attributes: map[string]schema.Attribute{
"aliases": schema.SetAttribute{
ElementType: types.StringType,
Description: "The aliases for the infrastructure resource.",
Optional: true,
},
"data": schema.StringAttribute{
Description: "The data of the infrastructure resource in JSON format.",
Required: true,
Validators: []validator.String{
JsonStringValidator(),
JsonHasNameKeyValidator(),
},
},
"id": schema.StringAttribute{
Description: "The ID of the infrastructure.",
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"owner": schema.StringAttribute{
Description: "The id of the team that owns the infrastructure resource. Does not support aliases!",
Required: true,
Validators: []validator.String{IdStringValidator()},
},
"schema": schema.StringAttribute{
Description: "The schema of the infrastructure resource that determines its data specification.",
Required: true,
},
},
Blocks: map[string]schema.Block{
"provider_data": schema.ListNestedBlock{
NestedObject: schema.NestedBlockObject{
Attributes: map[string]schema.Attribute{
"account": schema.StringAttribute{
Description: "The canonical account name for the provider of the infrastructure resource.",
Required: true,
},
"name": schema.StringAttribute{
Description: "The name of the provider of the infrastructure resource. (eg. AWS, GCP, Azure)",
Optional: true,
},
"type": schema.StringAttribute{
Description: "The type of the infrastructure resource as defined by its provider.",
Optional: true,
},
"url": schema.StringAttribute{
Description: "The url for the provider of the infrastructure resource.",
Optional: true,
},
},
},
},
},
},
StateUpgrader: func(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) {
upgradedStateModel := InfrastructureResourceModel{}
infraProviderDataList := types.ListNull(types.ObjectType{AttrTypes: infraProviderDataType})

resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("aliases"), &upgradedStateModel.Aliases)...)
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("data"), &upgradedStateModel.Data)...)
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("id"), &upgradedStateModel.Id)...)
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("owner"), &upgradedStateModel.Owner)...)
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("schema"), &upgradedStateModel.Schema)...)
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("provider_data"), &infraProviderDataList)...)
if len(infraProviderDataList.Elements()) == 1 {
infraProviderData := infraProviderDataList.Elements()[0].(basetypes.ObjectValue)
infraProviderDataAttrs := infraProviderData.Attributes()
upgradedStateModel.ProviderData = &InfraProviderData{
Account: infraProviderDataAttrs["account"].(basetypes.StringValue),
Name: infraProviderDataAttrs["name"].(basetypes.StringValue),
Type: infraProviderDataAttrs["type"].(basetypes.StringValue),
Url: infraProviderDataAttrs["url"].(basetypes.StringValue),
}
}

resp.Diagnostics.Append(resp.State.Set(ctx, upgradedStateModel)...)
},
},
}
}

func (r *InfrastructureResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var planModel InfrastructureResourceModel

Expand Down

0 comments on commit 008f156

Please sign in to comment.