Skip to content

Commit

Permalink
feat: reimplement resource "mackerel_role"
Browse files Browse the repository at this point in the history
  • Loading branch information
tosuke committed Jul 4, 2024
1 parent 706cb20 commit e1fff87
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 22 deletions.
6 changes: 1 addition & 5 deletions internal/mackerel/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,7 @@ func (m *RoleModel) readInner(ctx context.Context, client roleFinder) error {
m.ID = r.ID // computed
m.ServiceName = r.ServiceName // required
m.RoleName = r.RoleName // required

// optional
if /* preserve null */ !m.Memo.IsNull() || r.Memo.ValueString() != "" {
m.Memo = r.Memo
}
m.Memo = r.Memo // has default

return nil
}
Expand Down
17 changes: 0 additions & 17 deletions internal/mackerel/role_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,23 +126,6 @@ func Test_RoleModel_Read(t *testing.T) {
Memo: types.StringValue("memo"),
},
},
// TODO: smart handling for preserving null
/* "preserve null memo": {
in: RoleModel{
ID: types.StringValue("service0:role1"),
ServiceName: types.StringValue("service0"),
RoleName: types.StringValue("role1"),
Memo: types.StringNull(),
},
inClient: defaultClient,
wants: RoleModel{
ID: types.StringValue("service0:role1"),
ServiceName: types.StringValue("service0"),
RoleName: types.StringValue("role1"),
Memo: types.StringNull(), // not types.StringValue("")
},
}, */
}

ctx := context.Background()
Expand Down
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ func (m *mackerelProvider) Configure(ctx context.Context, req provider.Configure
func (m *mackerelProvider) Resources(context.Context) []func() resource.Resource {
return []func() resource.Resource{
NewMackerelNotificationGroupResource,
NewMackerelRoleResource,
NewMackerelServiceResource,
NewMackerelServiceMetadataResource,
}
Expand Down
147 changes: 147 additions & 0 deletions internal/provider/resource_mackerel_role.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package provider

import (
"context"

"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/stringdefault"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/mackerelio-labs/terraform-provider-mackerel/internal/mackerel"
)

var (
_ resource.Resource = (*mackerelRoleResource)(nil)
_ resource.ResourceWithConfigure = (*mackerelRoleResource)(nil)
_ resource.ResourceWithImportState = (*mackerelRoleResource)(nil)
)

func NewMackerelRoleResource() resource.Resource {
return &mackerelRoleResource{}
}

type mackerelRoleResource struct {
Client *mackerel.Client
}

func (r *mackerelRoleResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_role"
}

func (r *mackerelRoleResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "This resource allows creating and management of Roles.",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(), // immutable
},
},
"service": schema.StringAttribute{
Description: "The name of a Service.",
Required: true,
Validators: []validator.String{
mackerel.ServiceNameValidator(),
},
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(), // force new
},
},
"name": schema.StringAttribute{
Description: "The name of a Role.",
Required: true,
Validators: []validator.String{
mackerel.RoleNameValidator(),
},
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"memo": schema.StringAttribute{
Description: "Notes related to this role.",
Optional: true,
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
Default: stringdefault.StaticString(""),
},
},
}
}

func (r *mackerelRoleResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
client, diags := retrieveClient(ctx, req.ProviderData)
resp.Diagnostics.Append(diags...)
if diags.HasError() {
return
}
r.Client = client
}

func (r *mackerelRoleResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var data mackerel.RoleModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}

if err := data.Create(ctx, r.Client); err != nil {
resp.Diagnostics.AddError(
"Unable to create Role",
err.Error(),
)
return
}

resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

func (r *mackerelRoleResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var data mackerel.RoleModel
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}

if err := data.Read(ctx, r.Client); err != nil {
resp.Diagnostics.AddError(
"Unable to read Role",
err.Error(),
)
return
}

resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

func (r *mackerelRoleResource) Update(_ context.Context, _ resource.UpdateRequest, resp *resource.UpdateResponse) {
resp.Diagnostics.AddError(
"Unable to update Role",
"Mackerel Service Roles cannot be updated in-place. Please report this issue.",
)
}

func (r *mackerelRoleResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var data mackerel.RoleModel
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}

if err := data.Delete(ctx, r.Client); err != nil {
resp.Diagnostics.AddError(
"Unable to delete Role",
err.Error(),
)
return
}
}

func (r *mackerelRoleResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
}
26 changes: 26 additions & 0 deletions internal/provider/resource_mackerel_role_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package provider_test

import (
"context"
"testing"

fwresource "github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/mackerelio-labs/terraform-provider-mackerel/internal/provider"
)

func Test_MackerelRoleResource_schema(t *testing.T) {
t.Parallel()

ctx := context.Background()

req := fwresource.SchemaRequest{}
resp := fwresource.SchemaResponse{}
provider.NewMackerelRoleResource().Schema(ctx, req, &resp)
if resp.Diagnostics.HasError() {
t.Fatalf("schema: %+v", resp.Diagnostics)
}

if diags := resp.Schema.ValidateImplementation(ctx); diags.HasError() {
t.Fatalf("schema validation: %+v", diags)
}
}
1 change: 1 addition & 0 deletions mackerel/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ func protoV5ProviderServer(provider *schema.Provider) tfprotov5.ProviderServer {

// Resources
delete(provider.ResourcesMap, "mackerel_notification_group")
delete(provider.ResourcesMap, "mackerel_role")
delete(provider.ResourcesMap, "mackerel_service")
delete(provider.ResourcesMap, "mackerel_service_metadata")

Expand Down

0 comments on commit e1fff87

Please sign in to comment.