package provider import ( "context" "github.com/hashicorp/terraform-plugin-framework/attr" "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/setdefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/types" "git.veillax.com/Veillax/hhctl/client" ) var ( _ resource.Resource = &siteResource{} _ resource.ResourceWithConfigure = &siteResource{} _ resource.ResourceWithImportState = &siteResource{} ) func NewSite() resource.Resource { return &siteResource{} } type siteResource struct{ c *client.Client } type siteModel struct { ID types.String `tfsdk:"id"` Name types.String `tfsdk:"name"` Tags types.Set `tfsdk:"tags"` Token types.String `tfsdk:"token"` } func (r *siteResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { resp.TypeName = req.ProviderTypeName + "_site" } func (r *siteResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ Description: "A site: a private network that runs the Bamboo agent and holds the real services.", Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}}, "name": schema.StringAttribute{Required: true}, "tags": schema.SetAttribute{Optional: true, Computed: true, ElementType: types.StringType, Default: setdefault.StaticValue(types.SetValueMust(types.StringType, []attr.Value{})), Description: "Labels for grouping and filtering in the dashboard."}, "token": schema.StringAttribute{Computed: true, Sensitive: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, Description: "The token to give the Bamboo agent."}, }, } } func (r *siteResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { r.c = clientFrom(req.ProviderData) } func stringsOf(ctx context.Context, s types.Set) []string { out := []string{} if !s.IsNull() && !s.IsUnknown() { s.ElementsAs(ctx, &out, false) } return out } func setOf(v []string) types.Set { elems := make([]attr.Value, 0, len(v)) for _, s := range v { elems = append(elems, types.StringValue(s)) } return types.SetValueMust(types.StringType, elems) } func (r *siteResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { var plan siteModel if resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...); resp.Diagnostics.HasError() { return } s, err := r.c.CreateSite(plan.Name.ValueString(), stringsOf(ctx, plan.Tags)) if s != nil && s.ID != "" { plan.ID, plan.Token = types.StringValue(s.ID), types.StringValue(s.Token) resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) // keep it in state even if tagging failed } if err != nil { resp.Diagnostics.AddError("Creating site", err.Error()) } } func (r *siteResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { var state siteModel if resp.Diagnostics.Append(req.State.Get(ctx, &state)...); resp.Diagnostics.HasError() { return } s, err := r.c.GetSite(state.ID.ValueString()) if client.IsNotFound(err) { resp.State.RemoveResource(ctx) return } if err != nil { resp.Diagnostics.AddError("Reading site", err.Error()) return } state.Name, state.Tags = types.StringValue(s.Name), setOf(s.Tags) if s.Token != "" { state.Token = types.StringValue(s.Token) } resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) } func (r *siteResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { var plan siteModel if resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...); resp.Diagnostics.HasError() { return } if err := r.c.UpdateSite(plan.ID.ValueString(), plan.Name.ValueString(), stringsOf(ctx, plan.Tags)); err != nil { resp.Diagnostics.AddError("Updating site", err.Error()) return } resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } func (r *siteResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { var state siteModel if resp.Diagnostics.Append(req.State.Get(ctx, &state)...); resp.Diagnostics.HasError() { return } if err := r.c.DeleteSite(state.ID.ValueString()); err != nil && !client.IsNotFound(err) { resp.Diagnostics.AddError("Deleting site", err.Error()) } } func (r *siteResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { importByID(ctx, req, resp) }