blob: e6bf758e811aaf846f46a364ee3262e63288194c [file] [log] [blame]
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package provider
import (
"context"
"fmt"
"strings"
petname "github.com/dustinkirkland/golang-petname"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
"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/types"
mapplanmodifiers "github.com/terraform-providers/terraform-provider-random/internal/planmodifiers/map"
)
var _ resource.Resource = (*petResource)(nil)
func NewPetResource() resource.Resource {
return &petResource{}
}
type petResource struct{}
func (r *petResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_pet"
}
func (r *petResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "The resource `random_pet` generates random pet names that are intended to be used as " +
"unique identifiers for other resources.\n" +
"\n" +
"This resource can be used in conjunction with resources that have the `create_before_destroy` " +
"lifecycle flag set, to avoid conflicts with unique names during the brief period where both the old " +
"and new resources exist concurrently.",
Attributes: map[string]schema.Attribute{
"keepers": schema.MapAttribute{
Description: "Arbitrary map of values that, when changed, will trigger recreation of " +
"resource. See [the main provider documentation](../index.html) for more information.",
ElementType: types.StringType,
Optional: true,
PlanModifiers: []planmodifier.Map{
mapplanmodifiers.RequiresReplaceIfValuesNotNull(),
},
},
"length": schema.Int64Attribute{
Description: "The length (in words) of the pet name. Defaults to 2",
Optional: true,
Computed: true,
Default: int64default.StaticInt64(2),
PlanModifiers: []planmodifier.Int64{
int64planmodifier.RequiresReplace(),
},
},
"prefix": schema.StringAttribute{
Description: "A string to prefix the name with.",
Optional: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"separator": schema.StringAttribute{
Description: "The character to separate words in the pet name. Defaults to \"-\"",
Optional: true,
Computed: true,
Default: stringdefault.StaticString("-"),
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"id": schema.StringAttribute{
Description: "The random pet name.",
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
},
}
}
func (r *petResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
// This is necessary to ensure each call to petname is properly randomised:
// the library uses `rand.Intn()` and does NOT seed `rand.Seed()` by default,
// so this call takes care of that.
petname.NonDeterministicMode()
var plan petModelV0
diags := req.Plan.Get(ctx, &plan)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
length := plan.Length.ValueInt64()
separator := plan.Separator.ValueString()
prefix := plan.Prefix.ValueString()
pet := strings.ToLower(petname.Generate(int(length), separator))
pn := petModelV0{
Keepers: plan.Keepers,
Length: types.Int64Value(length),
Separator: types.StringValue(separator),
}
if prefix != "" {
pet = fmt.Sprintf("%s%s%s", prefix, separator, pet)
pn.Prefix = types.StringValue(prefix)
} else {
pn.Prefix = types.StringNull()
}
pn.ID = types.StringValue(pet)
diags = resp.State.Set(ctx, pn)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
// Read does not need to perform any operations as the state in ReadResourceResponse is already populated.
func (r *petResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
}
// Update ensures the plan value is copied to the state to complete the update.
func (r *petResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var model petModelV0
resp.Diagnostics.Append(req.Plan.Get(ctx, &model)...)
if resp.Diagnostics.HasError() {
return
}
resp.Diagnostics.Append(resp.State.Set(ctx, &model)...)
}
// Delete does not need to explicitly call resp.State.RemoveResource() as this is automatically handled by the
// [framework](https://github.com/hashicorp/terraform-plugin-framework/pull/301).
func (r *petResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
}
type petModelV0 struct {
ID types.String `tfsdk:"id"`
Keepers types.Map `tfsdk:"keepers"`
Length types.Int64 `tfsdk:"length"`
Prefix types.String `tfsdk:"prefix"`
Separator types.String `tfsdk:"separator"`
}