Skip to content

Commit

Permalink
add StateUpgrader for opslevel_check_service_ownership (#401)
Browse files Browse the repository at this point in the history
* WIP: add StateUpgrader for opslevel_check_service_ownership

* WIP: fixing StateImporter

* fix StateUpgrader for opslevel_check_service_ownership resource

* add StateUpgraders for remaining check resources

* add changie log

* add Version to check schemas

* fix field type of schema in check repo file StateUpgrader

* add StateUpgrader to opslevel_check_repository_grep resource
  • Loading branch information
davidbloss authored Jul 15, 2024
1 parent ea6b6d9 commit ae857bc
Show file tree
Hide file tree
Showing 10 changed files with 555 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .changes/unreleased/Bugfix-20240715-112534.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
kind: Bugfix
body: fix Terraform state upgrades for previous provider versions on checks resources
time: 2024-07-15T11:25:34.595155-05:00
55 changes: 55 additions & 0 deletions opslevel/resource_opslevel_check_alert_source_usage.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
Expand Down Expand Up @@ -92,6 +93,7 @@ func (r *CheckAlertSourceUsageResource) Metadata(ctx context.Context, req resour

func (r *CheckAlertSourceUsageResource) 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 Alert Source Usage Resource",

Expand All @@ -109,6 +111,59 @@ func (r *CheckAlertSourceUsageResource) Schema(ctx context.Context, req resource
}
}

func (r *CheckAlertSourceUsageResource) 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 Alert Source Usage Resource",
Attributes: getCheckBaseSchemaV0(map[string]schema.Attribute{
"alert_type": schema.StringAttribute{
Description: "The type of the alert source.",
Required: true,
},
"id": schema.StringAttribute{
Description: "The ID of this resource.",
Computed: true,
},
}),
Blocks: map[string]schema.Block{
"alert_name_predicate": schema.ListNestedBlock{
NestedObject: predicateSchemaV0,
},
},
},
StateUpgrader: func(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) {
var diags diag.Diagnostics
upgradedStateModel := CheckAlertSourceUsageResourceModel{}
alertNamePredicateList := types.ListNull(types.ObjectType{AttrTypes: predicateType})

// 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)...)

// alert source specific attributes
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("alert_type"), &upgradedStateModel.AlertType)...)
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("alert_name_predicate"), &alertNamePredicateList)...)
if len(alertNamePredicateList.Elements()) == 1 {
alertNamePredicate := alertNamePredicateList.Elements()[0]
upgradedStateModel.AlertNamePredicate, diags = types.ObjectValueFrom(ctx, predicateType, alertNamePredicate)
resp.Diagnostics.Append(diags...)
}

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

func (r *CheckAlertSourceUsageResource) ValidateConfig(ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse) {
var configModel CheckAlertSourceUsageResourceModel
resp.Diagnostics.Append(req.Config.Get(ctx, &configModel)...)
Expand Down
63 changes: 63 additions & 0 deletions opslevel/resource_opslevel_check_base.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,66 @@ func PredicateSchema() schema.SingleNestedAttribute {
},
}
}

// pre-v1.x base schema fields for checks used by StateUpgraders
func getCheckBaseSchemaV0(extras map[string]schema.Attribute) map[string]schema.Attribute {
output := map[string]schema.Attribute{
"last_updated": schema.StringAttribute{
Optional: true,
Computed: true,
},
"name": schema.StringAttribute{
Description: "The display name of the check.",
Required: true,
},
"enabled": schema.BoolAttribute{
Description: `Whether the check is enabled or not. Do not use this field in tandem with 'enable_on'.`,
Required: true,
},
"enable_on": schema.StringAttribute{
Description: `The date when the check will be automatically enabled.
If you use this field you should add both 'enabled' and 'enable_on' to the lifecycle ignore_changes settings.
See example in opslevel_check_manual for proper configuration.
`,
Optional: true,
},
"category": schema.StringAttribute{
Description: "The id of the category the check belongs to.",
Required: true,
},
"level": schema.StringAttribute{
Description: "The id of the level the check belongs to.",
Required: true,
},
"owner": schema.StringAttribute{
Description: "The id of the team that owns the check.",
Optional: true,
},
"filter": schema.StringAttribute{
Description: "The id of the filter of the check.",
Optional: true,
},
"notes": schema.StringAttribute{
Description: "Additional information about the check.",
Optional: true,
},
}
for k, v := range extras {
output[k] = v
}
return output
}

var predicateSchemaV0 = schema.NestedBlockObject{
Attributes: map[string]schema.Attribute{
"type": schema.StringAttribute{
Description: "A condition that should be satisfied.",
Required: true,
Validators: []validator.String{stringvalidator.OneOf(opslevel.AllPredicateTypeEnum...)},
},
"value": schema.StringAttribute{
Description: "The condition value used by the predicate.",
Optional: true,
},
},
}
66 changes: 66 additions & 0 deletions opslevel/resource_opslevel_check_repository_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"

"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
Expand Down Expand Up @@ -94,6 +95,7 @@ func (r *CheckRepositoryFileResource) Metadata(ctx context.Context, req resource

func (r *CheckRepositoryFileResource) 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 Repository File Resource",

Expand All @@ -116,6 +118,70 @@ func (r *CheckRepositoryFileResource) Schema(ctx context.Context, req resource.S
}
}

func (r *CheckRepositoryFileResource) 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{
"directory_search": schema.BoolAttribute{
Description: "Whether the check looks for the existence of a directory instead of a file.",
Required: true,
},
"filepaths": schema.ListAttribute{
Description: "Restrict the search to certain file paths.",
ElementType: types.StringType,
Required: true,
},
"id": schema.StringAttribute{
Description: "The ID of this resource.",
Computed: true,
},
"use_absolute_root": schema.BoolAttribute{
Description: "Whether the checks looks at the absolute root of a repo or the relative root (the directory specified when attached a repo to a service).",
Required: true,
},
}),
Blocks: map[string]schema.Block{
"file_contents_predicate": schema.ListNestedBlock{
NestedObject: predicateSchemaV0,
},
},
},
StateUpgrader: func(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) {
var diags diag.Diagnostics
upgradedStateModel := CheckRepositoryFileResourceModel{}
fileContentsPredicateList := types.ListNull(types.ObjectType{AttrTypes: predicateType})

// 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("directory_search"), &upgradedStateModel.DirectorySearch)...)
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("filepaths"), &upgradedStateModel.Filepaths)...)
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("use_absolute_root"), &upgradedStateModel.UseAbsoluteRoot)...)
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("file_contents_predicate"), &fileContentsPredicateList)...)
if len(fileContentsPredicateList.Elements()) == 1 {
fileContentsPredicate := fileContentsPredicateList.Elements()[0]
upgradedStateModel.FileContentsPredicate, diags = types.ObjectValueFrom(ctx, predicateType, fileContentsPredicate)
resp.Diagnostics.Append(diags...)
}

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

func (r *CheckRepositoryFileResource) ValidateConfig(ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse) {
var configModel CheckRepositoryFileResourceModel
resp.Diagnostics.Append(req.Config.Get(ctx, &configModel)...)
Expand Down
60 changes: 60 additions & 0 deletions opslevel/resource_opslevel_check_repository_grep.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"slices"

"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
Expand Down Expand Up @@ -111,6 +112,65 @@ func (r *CheckRepositoryGrepResource) Schema(ctx context.Context, req resource.S
}
}

func (r *CheckRepositoryGrepResource) 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 Grep Resource",
Attributes: getCheckBaseSchemaV0(map[string]schema.Attribute{
"directory_search": schema.BoolAttribute{
Description: "Whether the check looks for the existence of a directory instead of a file.",
Required: true,
},
"filepaths": schema.ListAttribute{
Description: "Restrict the search to certain file paths.",
ElementType: types.StringType,
Required: true,
},
"id": schema.StringAttribute{
Description: "The ID of this resource.",
Computed: true,
},
}),
Blocks: map[string]schema.Block{
"file_contents_predicate": schema.ListNestedBlock{
NestedObject: predicateSchemaV0,
},
},
},
StateUpgrader: func(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) {
var diags diag.Diagnostics
upgradedStateModel := CheckRepositoryGrepResourceModel{}
fileContentsPredicateList := types.ListNull(types.ObjectType{AttrTypes: predicateType})

// 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 grep specific attributes
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("directory_search"), &upgradedStateModel.DirectorySearch)...)
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("filepaths"), &upgradedStateModel.Filepaths)...)
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("file_contents_predicate"), &fileContentsPredicateList)...)
if len(fileContentsPredicateList.Elements()) == 1 {
fileContentsPredicate := fileContentsPredicateList.Elements()[0]
upgradedStateModel.FileContentsPredicate, diags = types.ObjectValueFrom(ctx, predicateType, fileContentsPredicate)
resp.Diagnostics.Append(diags...)
}

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

func (r *CheckRepositoryGrepResource) ValidateConfig(ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse) {
var configModel CheckRepositoryGrepResourceModel
resp.Diagnostics.Append(req.Config.Get(ctx, &configModel)...)
Expand Down
55 changes: 55 additions & 0 deletions opslevel/resource_opslevel_check_repository_search.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ func (r *CheckRepositorySearchResource) Schema(ctx context.Context, req resource
predicateSchema.Required = true

resp.Schema = schema.Schema{
Version: 1,
// This description is used by the documentation generator and the language server.
MarkdownDescription: "Check Repository Search Resource",

Expand All @@ -118,6 +119,60 @@ func (r *CheckRepositorySearchResource) Schema(ctx context.Context, req resource
}
}

func (r *CheckRepositorySearchResource) 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{
"file_extensions": schema.ListAttribute{
Description: "Restrict the search to certain file paths.",
ElementType: types.StringType,
Optional: true,
},
"id": schema.StringAttribute{
Description: "The ID of this resource.",
Computed: true,
},
}),
Blocks: map[string]schema.Block{
"file_contents_predicate": schema.ListNestedBlock{
NestedObject: predicateSchemaV0,
},
},
},
StateUpgrader: func(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) {
var diags diag.Diagnostics
upgradedStateModel := CheckRepositoryFileResourceModel{}
fileContentsPredicateList := types.ListNull(types.ObjectType{AttrTypes: predicateType})

// 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("file_extensions"), &upgradedStateModel.Filepaths)...)
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("file_contents_predicate"), &fileContentsPredicateList)...)
if len(fileContentsPredicateList.Elements()) == 1 {
fileContentsPredicate := fileContentsPredicateList.Elements()[0]
upgradedStateModel.FileContentsPredicate, diags = types.ObjectValueFrom(ctx, predicateType, fileContentsPredicate)
resp.Diagnostics.Append(diags...)
}

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

func (r *CheckRepositorySearchResource) ValidateConfig(ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse) {
var configModel CheckRepositorySearchResourceModel
resp.Diagnostics.Append(req.Config.Get(ctx, &configModel)...)
Expand Down
Loading

0 comments on commit ae857bc

Please sign in to comment.