From 517e583f42f5667ec240424191751430e4001208 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Wed, 12 Jun 2024 14:26:48 +0200 Subject: [PATCH 01/18] bump client version --- go.mod | 2 +- go.sum | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index dd3782e..7137129 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/mitchellh/go-homedir v1.1.0 - github.com/pluralsh/console-client-go v0.5.16 + github.com/pluralsh/console-client-go v0.6.0 github.com/pluralsh/plural-cli v0.8.5-0.20240216094552-efc34ee6de37 github.com/pluralsh/polly v0.1.7 github.com/samber/lo v1.38.1 diff --git a/go.sum b/go.sum index efa2aaa..509602d 100644 --- a/go.sum +++ b/go.sum @@ -858,6 +858,7 @@ github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZ github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pluralsh/console-client-go v0.5.16 h1:AHBnis/xUdXbUclNH1GUwcqAsCOzHzOtwetKOIS0dTM= github.com/pluralsh/console-client-go v0.5.16/go.mod h1:eyCiLA44YbXiYyJh8303jk5JdPkt9McgCo5kBjk4lKo= +github.com/pluralsh/console-client-go v0.6.0/go.mod h1:eyCiLA44YbXiYyJh8303jk5JdPkt9McgCo5kBjk4lKo= github.com/pluralsh/gqlclient v1.11.0 h1:FfXW7FiEJLHOfTAa7NxDb8jb3aMZNIpCAcG+bg8uHYA= github.com/pluralsh/gqlclient v1.11.0/go.mod h1:qSXKUlio1F2DRPy8el4oFYsmpKbkUYspgPB87T4it5I= github.com/pluralsh/plural-cli v0.8.5-0.20240216094552-efc34ee6de37 h1:DBnaKvKmbTbKwbkrh/2gJBwyHYfaXdxeT3UGh+94K4g= From 9c130bb6281c30b838d53d099a11495519648dc9 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Wed, 12 Jun 2024 14:31:00 +0200 Subject: [PATCH 02/18] rename cluster bindings to bindings --- .../{cluster_bindings.go => bindings.go} | 34 +++++++++---------- internal/resource/cluster_model.go | 8 ++--- .../resource/infrastructure_stack_model.go | 2 +- internal/resource/rbac_model.go | 6 ++-- 4 files changed, 25 insertions(+), 25 deletions(-) rename internal/common/{cluster_bindings.go => bindings.go} (59%) diff --git a/internal/common/cluster_bindings.go b/internal/common/bindings.go similarity index 59% rename from internal/common/cluster_bindings.go rename to internal/common/bindings.go index 14e1180..7d5dee7 100644 --- a/internal/common/cluster_bindings.go +++ b/internal/common/bindings.go @@ -10,34 +10,34 @@ import ( console "github.com/pluralsh/console-client-go" ) -type ClusterBindings struct { +type Bindings struct { Read types.Set `tfsdk:"read"` Write types.Set `tfsdk:"write"` } -func (cb *ClusterBindings) ReadAttributes(ctx context.Context, d diag.Diagnostics) []*console.PolicyBindingAttributes { +func (cb *Bindings) ReadAttributes(ctx context.Context, d diag.Diagnostics) []*console.PolicyBindingAttributes { if cb == nil { return nil } - return clusterPolicyBindingAttributes(cb.Read, ctx, d) + return policyBindingAttributes(cb.Read, ctx, d) } -func (cb *ClusterBindings) WriteAttributes(ctx context.Context, d diag.Diagnostics) []*console.PolicyBindingAttributes { +func (cb *Bindings) WriteAttributes(ctx context.Context, d diag.Diagnostics) []*console.PolicyBindingAttributes { if cb == nil { return nil } - return clusterPolicyBindingAttributes(cb.Write, ctx, d) + return policyBindingAttributes(cb.Write, ctx, d) } -func clusterPolicyBindingAttributes(bindings types.Set, ctx context.Context, d diag.Diagnostics) []*console.PolicyBindingAttributes { +func policyBindingAttributes(bindings types.Set, ctx context.Context, d diag.Diagnostics) []*console.PolicyBindingAttributes { if bindings.IsNull() { return nil } result := make([]*console.PolicyBindingAttributes, 0, len(bindings.Elements())) - elements := make([]ClusterPolicyBinding, len(bindings.Elements())) + elements := make([]PolicyBinding, len(bindings.Elements())) d.Append(bindings.ElementsAs(ctx, &elements, false)...) for _, binding := range elements { @@ -51,16 +51,16 @@ func clusterPolicyBindingAttributes(bindings types.Set, ctx context.Context, d d return result } -func (cb *ClusterBindings) From(readBindings []*console.PolicyBindingFragment, writeBindings []*console.PolicyBindingFragment, ctx context.Context, d diag.Diagnostics) { +func (cb *Bindings) From(readBindings []*console.PolicyBindingFragment, writeBindings []*console.PolicyBindingFragment, ctx context.Context, d diag.Diagnostics) { if cb == nil { return } - cb.Read = clusterBindingsFrom(readBindings, cb.Read, ctx, d) - cb.Write = clusterBindingsFrom(writeBindings, cb.Write, ctx, d) + cb.Read = bindingsFrom(readBindings, cb.Read, ctx, d) + cb.Write = bindingsFrom(writeBindings, cb.Write, ctx, d) } -func clusterBindingsFrom(bindings []*console.PolicyBindingFragment, config types.Set, ctx context.Context, d diag.Diagnostics) types.Set { +func bindingsFrom(bindings []*console.PolicyBindingFragment, config types.Set, ctx context.Context, d diag.Diagnostics) types.Set { if len(bindings) == 0 { // Rewriting config to state to avoid inconsistent result errors. // This could happen, for example, when sending "nil" to API and "[]" is returned as a result. @@ -69,7 +69,7 @@ func clusterBindingsFrom(bindings []*console.PolicyBindingFragment, config types values := make([]attr.Value, len(bindings)) for i, binding := range bindings { - value := ClusterPolicyBinding{ + value := PolicyBinding{ ID: types.StringPointerValue(binding.ID), } @@ -81,29 +81,29 @@ func clusterBindingsFrom(bindings []*console.PolicyBindingFragment, config types value.GroupID = types.StringValue(binding.Group.ID) } - objValue, diags := types.ObjectValueFrom(ctx, ClusterPolicyBindingAttrTypes, value) + objValue, diags := types.ObjectValueFrom(ctx, PolicyBindingAttrTypes, value) values[i] = objValue d.Append(diags...) } - setValue, diags := types.SetValue(basetypes.ObjectType{AttrTypes: ClusterPolicyBindingAttrTypes}, values) + setValue, diags := types.SetValue(basetypes.ObjectType{AttrTypes: PolicyBindingAttrTypes}, values) d.Append(diags...) return setValue } -type ClusterPolicyBinding struct { +type PolicyBinding struct { GroupID types.String `tfsdk:"group_id"` ID types.String `tfsdk:"id"` UserID types.String `tfsdk:"user_id"` } -var ClusterPolicyBindingAttrTypes = map[string]attr.Type{ +var PolicyBindingAttrTypes = map[string]attr.Type{ "group_id": types.StringType, "id": types.StringType, "user_id": types.StringType, } -func (cpb *ClusterPolicyBinding) Attributes() *console.PolicyBindingAttributes { +func (cpb *PolicyBinding) Attributes() *console.PolicyBindingAttributes { return &console.PolicyBindingAttributes{ ID: cpb.ID.ValueStringPointer(), UserID: cpb.UserID.ValueStringPointer(), diff --git a/internal/resource/cluster_model.go b/internal/resource/cluster_model.go index 7160a3a..ab724c0 100644 --- a/internal/resource/cluster_model.go +++ b/internal/resource/cluster_model.go @@ -21,10 +21,10 @@ type cluster struct { // DesiredVersion types.String `tfsdk:"desired_version"` // ProviderId types.String `tfsdk:"provider_id"` // Cloud types.String `tfsdk:"cloud"` - Protect types.Bool `tfsdk:"protect"` - Tags types.Map `tfsdk:"tags"` - Metadata types.String `tfsdk:"metadata"` - Bindings *common.ClusterBindings `tfsdk:"bindings"` + Protect types.Bool `tfsdk:"protect"` + Tags types.Map `tfsdk:"tags"` + Metadata types.String `tfsdk:"metadata"` + Bindings *common.Bindings `tfsdk:"bindings"` // NodePools types.Map `tfsdk:"node_pools"` // CloudSettings *ClusterCloudSettings `tfsdk:"cloud_settings"` HelmRepoUrl types.String `tfsdk:"helm_repo_url"` diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index d838644..44937f5 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -27,7 +27,7 @@ type infrastructureStack struct { Files types.Map `tfsdk:"files"` Environment types.Set `tfsdk:"environment"` JobSpec *InfrastructureStackJobSpec `tfsdk:"job_spec"` - Bindings *common.ClusterBindings `tfsdk:"bindings"` + Bindings *common.Bindings `tfsdk:"bindings"` } func (is *infrastructureStack) Attributes(ctx context.Context, d diag.Diagnostics, client *client.Client) (*gqlclient.StackAttributes, error) { diff --git a/internal/resource/rbac_model.go b/internal/resource/rbac_model.go index b89b4bb..572611e 100644 --- a/internal/resource/rbac_model.go +++ b/internal/resource/rbac_model.go @@ -11,9 +11,9 @@ import ( ) type rbac struct { - ClusterId types.String `tfsdk:"cluster_id"` - ServiceId types.String `tfsdk:"service_id"` - Bindings *common.ClusterBindings `tfsdk:"rbac"` + ClusterId types.String `tfsdk:"cluster_id"` + ServiceId types.String `tfsdk:"service_id"` + Bindings *common.Bindings `tfsdk:"rbac"` } func (g *rbac) Attributes(ctx context.Context, d diag.Diagnostics) gqlclient.RbacAttributes { From 6f4293941622b843d29bbcadb5f52c06013df339 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Wed, 12 Jun 2024 14:35:26 +0200 Subject: [PATCH 03/18] add project resource model --- internal/resource/project_model.go | 37 ++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 internal/resource/project_model.go diff --git a/internal/resource/project_model.go b/internal/resource/project_model.go new file mode 100644 index 0000000..16f66c7 --- /dev/null +++ b/internal/resource/project_model.go @@ -0,0 +1,37 @@ +package resource + +import ( + "context" + + "terraform-provider-plural/internal/client" + "terraform-provider-plural/internal/common" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/types" + gqlclient "github.com/pluralsh/console-client-go" +) + +type project struct { + Id types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Default types.Bool `tfsdk:"default"` + Description types.String `tfsdk:"description"` + Bindings *common.Bindings `tfsdk:"bindings"` +} + +func (p *project) Attributes(ctx context.Context, d diag.Diagnostics, client *client.Client) (*gqlclient.ProjectAttributes, error) { + return &gqlclient.ProjectAttributes{ + Name: p.Name.ValueString(), + Description: p.Description.ValueStringPointer(), + ReadBindings: p.Bindings.ReadAttributes(ctx, d), + WriteBindings: p.Bindings.WriteAttributes(ctx, d), + }, nil +} + +func (p *project) From(project *gqlclient.ProjectFragment, ctx context.Context, d diag.Diagnostics) { + p.Id = types.StringValue(project.ID) + p.Name = types.StringValue(project.Name) + p.Default = types.BoolPointerValue(project.Default) + p.Description = types.StringPointerValue(project.Description) + p.Bindings.From(project.ReadBindings, project.WriteBindings, ctx, d) +} From 930baac44dfe6c09df5ba56a6f065876965853de Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Wed, 12 Jun 2024 16:15:24 +0200 Subject: [PATCH 04/18] finish project resource --- internal/provider/provider.go | 1 + internal/resource/project.go | 118 ++++++++++++++++++++++++++++ internal/resource/project_model.go | 7 +- internal/resource/project_schema.go | 80 +++++++++++++++++++ 4 files changed, 202 insertions(+), 4 deletions(-) create mode 100644 internal/resource/project.go create mode 100644 internal/resource/project_schema.go diff --git a/internal/provider/provider.go b/internal/provider/provider.go index a2878b2..e92b8e7 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -179,6 +179,7 @@ func (p *PluralProvider) Configure(ctx context.Context, req provider.ConfigureRe func (p *PluralProvider) Resources(_ context.Context) []func() resource.Resource { return []func() resource.Resource{ + r.NewProjectResource, r.NewClusterResource, r.NewGitRepositoryResource, r.NewProviderResource, diff --git a/internal/resource/project.go b/internal/resource/project.go new file mode 100644 index 0000000..cf2e4db --- /dev/null +++ b/internal/resource/project.go @@ -0,0 +1,118 @@ +package resource + +import ( + "context" + "fmt" + + "terraform-provider-plural/internal/client" + "terraform-provider-plural/internal/common" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +var _ resource.Resource = &ProjectResource{} +var _ resource.ResourceWithImportState = &ProjectResource{} + +func NewProjectResource() resource.Resource { + return &ProjectResource{} +} + +// ProjectResource defines the project resource implementation. +type ProjectResource struct { + client *client.Client +} + +func (r *ProjectResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_project" +} + +func (r *ProjectResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = r.schema() +} + +func (r *ProjectResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + data, ok := req.ProviderData.(*common.ProviderData) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Project Resource Configure Type", + fmt.Sprintf("Expected *common.ProviderData, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + r.client = data.Client +} + +func (r *ProjectResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + data := new(project) + resp.Diagnostics.Append(req.Plan.Get(ctx, data)...) + if resp.Diagnostics.HasError() { + return + } + + attr, err := data.Attributes(ctx, resp.Diagnostics) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to get attributes, got error: %s", err)) + return + } + sd, err := r.client.CreateProject(ctx, *attr) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create project, got error: %s", err)) + return + } + + data.From(sd.CreateProject, ctx, resp.Diagnostics) + resp.Diagnostics.Append(resp.State.Set(ctx, data)...) +} + +func (r *ProjectResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + data := new(project) + resp.Diagnostics.Append(req.State.Get(ctx, data)...) + if resp.Diagnostics.HasError() { + return + } + + response, err := r.client.GetProject(ctx, data.Id.ValueStringPointer(), nil) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read project, got error: %s", err)) + return + } + + data.From(response.Project, ctx, resp.Diagnostics) + resp.Diagnostics.Append(resp.State.Set(ctx, data)...) +} + +func (r *ProjectResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + data := new(project) + resp.Diagnostics.Append(req.Plan.Get(ctx, data)...) + if resp.Diagnostics.HasError() { + return + } + + attr, err := data.Attributes(ctx, resp.Diagnostics) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to get attributes, got error: %s", err)) + return + } + _, err = r.client.UpdateProject(ctx, data.Id.ValueString(), *attr) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update project, got error: %s", err)) + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, data)...) +} + +func (r *ProjectResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // Ignore. +} + +func (r *ProjectResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} diff --git a/internal/resource/project_model.go b/internal/resource/project_model.go index 16f66c7..c8975d1 100644 --- a/internal/resource/project_model.go +++ b/internal/resource/project_model.go @@ -3,7 +3,6 @@ package resource import ( "context" - "terraform-provider-plural/internal/client" "terraform-provider-plural/internal/common" "github.com/hashicorp/terraform-plugin-framework/diag" @@ -14,12 +13,12 @@ import ( type project struct { Id types.String `tfsdk:"id"` Name types.String `tfsdk:"name"` - Default types.Bool `tfsdk:"default"` Description types.String `tfsdk:"description"` + Default types.Bool `tfsdk:"default"` Bindings *common.Bindings `tfsdk:"bindings"` } -func (p *project) Attributes(ctx context.Context, d diag.Diagnostics, client *client.Client) (*gqlclient.ProjectAttributes, error) { +func (p *project) Attributes(ctx context.Context, d diag.Diagnostics) (*gqlclient.ProjectAttributes, error) { return &gqlclient.ProjectAttributes{ Name: p.Name.ValueString(), Description: p.Description.ValueStringPointer(), @@ -31,7 +30,7 @@ func (p *project) Attributes(ctx context.Context, d diag.Diagnostics, client *cl func (p *project) From(project *gqlclient.ProjectFragment, ctx context.Context, d diag.Diagnostics) { p.Id = types.StringValue(project.ID) p.Name = types.StringValue(project.Name) - p.Default = types.BoolPointerValue(project.Default) p.Description = types.StringPointerValue(project.Description) + p.Default = types.BoolPointerValue(project.Default) p.Bindings.From(project.ReadBindings, project.WriteBindings, ctx, d) } diff --git a/internal/resource/project_schema.go b/internal/resource/project_schema.go new file mode 100644 index 0000000..149ba78 --- /dev/null +++ b/internal/resource/project_schema.go @@ -0,0 +1,80 @@ +package resource + +import ( + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" +) + +func (r *ProjectResource) schema() schema.Schema { + return schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Description: "Internal identifier of this project.", + MarkdownDescription: "Internal identifier of this project.", + Computed: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, + }, + "name": schema.StringAttribute{ + Description: "Human-readable name of this project.", + MarkdownDescription: "Human-readable name of this project.", + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, + }, + "description": schema.StringAttribute{ + Description: "Description of this project.", + MarkdownDescription: "Description of this project.", + Optional: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, + }, + "default": schema.StringAttribute{ + Computed: true, + }, + "bindings": schema.SingleNestedAttribute{ + Description: "Read and write policies of this project.", + MarkdownDescription: "Read and write policies of this project.", + Optional: true, + Attributes: map[string]schema.Attribute{ + "read": schema.SetNestedAttribute{ + Description: "Read policies of this project.", + MarkdownDescription: "Read policies of this project.", + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "group_id": schema.StringAttribute{ + Optional: true, + }, + "id": schema.StringAttribute{ + Optional: true, + }, + "user_id": schema.StringAttribute{ + Optional: true, + }, + }, + }, + }, + "write": schema.SetNestedAttribute{ + Description: "Write policies of this project.", + MarkdownDescription: "Write policies of this project.", + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "group_id": schema.StringAttribute{ + Optional: true, + }, + "id": schema.StringAttribute{ + Optional: true, + }, + "user_id": schema.StringAttribute{ + Optional: true, + }, + }, + }, + }, + }, + PlanModifiers: []planmodifier.Object{objectplanmodifier.UseStateForUnknown()}, + }, + }, + } +} From a2472d648dfd6ae424a300eac7ec2a6981a19cd8 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Wed, 12 Jun 2024 16:35:06 +0200 Subject: [PATCH 05/18] add project data source --- internal/datasource/project.go | 135 +++++++++++++++++++++++++++++++++ internal/provider/provider.go | 1 + 2 files changed, 136 insertions(+) create mode 100644 internal/datasource/project.go diff --git a/internal/datasource/project.go b/internal/datasource/project.go new file mode 100644 index 0000000..0dcfadf --- /dev/null +++ b/internal/datasource/project.go @@ -0,0 +1,135 @@ +package datasource + +import ( + "context" + "fmt" + + "terraform-provider-plural/internal/client" + "terraform-provider-plural/internal/common" + "terraform-provider-plural/internal/resource" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" +) + +func NewProjectDataSource() datasource.DataSource { + return &projectDataSource{} +} + +type projectDataSource struct { + client *client.Client +} + +func (d *projectDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_project" +} + +func (d *projectDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Description: "Internal identifier of this project.", + MarkdownDescription: "Internal identifier of this project.", + Computed: true, + }, + "name": schema.StringAttribute{ + Description: "Human-readable name of this project.", + MarkdownDescription: "Human-readable name of this project.", + Required: true, + }, + "description": schema.StringAttribute{ + Description: "Description of this project.", + MarkdownDescription: "Description of this project.", + Optional: true, + }, + "default": schema.StringAttribute{ + Computed: true, + }, + "bindings": schema.SingleNestedAttribute{ + Description: "Read and write policies of this project.", + MarkdownDescription: "Read and write policies of this project.", + Optional: true, + Attributes: map[string]schema.Attribute{ + "read": schema.SetNestedAttribute{ + Description: "Read policies of this project.", + MarkdownDescription: "Read policies of this project.", + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "group_id": schema.StringAttribute{ + Optional: true, + }, + "id": schema.StringAttribute{ + Optional: true, + }, + "user_id": schema.StringAttribute{ + Optional: true, + }, + }, + }, + }, + "write": schema.SetNestedAttribute{ + Description: "Write policies of this project.", + MarkdownDescription: "Write policies of this project.", + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "group_id": schema.StringAttribute{ + Optional: true, + }, + "id": schema.StringAttribute{ + Optional: true, + }, + "user_id": schema.StringAttribute{ + Optional: true, + }, + }, + }, + }, + }, + }, + }, + } +} + +func (d *projectDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + data, ok := req.ProviderData.(*common.ProviderData) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Project Resource Configure Type", + fmt.Sprintf("Expected *common.ProviderData, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + return + } + + d.client = data.Client +} + +func (d *projectDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + data := new(resource.Project) + resp.Diagnostics.Append(req.Config.Get(ctx, data)...) + if resp.Diagnostics.HasError() { + return + } + + if data.Id.IsNull() && data.Name.IsNull() { + resp.Diagnostics.AddError( + "Missing Project ID and Name", + "The provider could not read project data. ID or name needs to be specified.", + ) + return + } + + response, err := d.client.GetProject(ctx, data.Id.ValueStringPointer(), data.Name.ValueStringPointer()) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read project, got error: %s", err)) + return + } + + data.From(response.Project, ctx, resp.Diagnostics) + resp.Diagnostics.Append(resp.State.Set(ctx, data)...) +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index e92b8e7..972aabb 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -192,6 +192,7 @@ func (p *PluralProvider) Resources(_ context.Context) []func() resource.Resource func (p *PluralProvider) DataSources(_ context.Context) []func() datasource.DataSource { return []func() datasource.DataSource{ + ds.NewProjectDataSource, ds.NewClusterDataSource, ds.NewGitRepositoryDataSource, ds.NewProviderDataSource, From 49f4b17f909608a61df743ee92d2b8c688425c0a Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Wed, 12 Jun 2024 16:35:28 +0200 Subject: [PATCH 06/18] refactor project resource --- internal/resource/project.go | 80 +++++++++++++++++++++++++++-- internal/resource/project_model.go | 6 +-- internal/resource/project_schema.go | 80 ----------------------------- 3 files changed, 79 insertions(+), 87 deletions(-) delete mode 100644 internal/resource/project_schema.go diff --git a/internal/resource/project.go b/internal/resource/project.go index cf2e4db..be1b128 100644 --- a/internal/resource/project.go +++ b/internal/resource/project.go @@ -9,6 +9,10 @@ import ( "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/objectplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" ) var _ resource.Resource = &ProjectResource{} @@ -28,7 +32,75 @@ func (r *ProjectResource) Metadata(_ context.Context, req resource.MetadataReque } func (r *ProjectResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { - resp.Schema = r.schema() + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Description: "Internal identifier of this project.", + MarkdownDescription: "Internal identifier of this project.", + Computed: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, + }, + "name": schema.StringAttribute{ + Description: "Human-readable name of this project.", + MarkdownDescription: "Human-readable name of this project.", + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, + }, + "description": schema.StringAttribute{ + Description: "Description of this project.", + MarkdownDescription: "Description of this project.", + Optional: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, + }, + "default": schema.StringAttribute{ + Computed: true, + }, + "bindings": schema.SingleNestedAttribute{ + Description: "Read and write policies of this project.", + MarkdownDescription: "Read and write policies of this project.", + Optional: true, + Attributes: map[string]schema.Attribute{ + "read": schema.SetNestedAttribute{ + Description: "Read policies of this project.", + MarkdownDescription: "Read policies of this project.", + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "group_id": schema.StringAttribute{ + Optional: true, + }, + "id": schema.StringAttribute{ + Optional: true, + }, + "user_id": schema.StringAttribute{ + Optional: true, + }, + }, + }, + }, + "write": schema.SetNestedAttribute{ + Description: "Write policies of this project.", + MarkdownDescription: "Write policies of this project.", + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "group_id": schema.StringAttribute{ + Optional: true, + }, + "id": schema.StringAttribute{ + Optional: true, + }, + "user_id": schema.StringAttribute{ + Optional: true, + }, + }, + }, + }, + }, + PlanModifiers: []planmodifier.Object{objectplanmodifier.UseStateForUnknown()}, + }, + }, + } } func (r *ProjectResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { @@ -50,7 +122,7 @@ func (r *ProjectResource) Configure(_ context.Context, req resource.ConfigureReq } func (r *ProjectResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - data := new(project) + data := new(Project) resp.Diagnostics.Append(req.Plan.Get(ctx, data)...) if resp.Diagnostics.HasError() { return @@ -72,7 +144,7 @@ func (r *ProjectResource) Create(ctx context.Context, req resource.CreateRequest } func (r *ProjectResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - data := new(project) + data := new(Project) resp.Diagnostics.Append(req.State.Get(ctx, data)...) if resp.Diagnostics.HasError() { return @@ -89,7 +161,7 @@ func (r *ProjectResource) Read(ctx context.Context, req resource.ReadRequest, re } func (r *ProjectResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - data := new(project) + data := new(Project) resp.Diagnostics.Append(req.Plan.Get(ctx, data)...) if resp.Diagnostics.HasError() { return diff --git a/internal/resource/project_model.go b/internal/resource/project_model.go index c8975d1..6afd4a7 100644 --- a/internal/resource/project_model.go +++ b/internal/resource/project_model.go @@ -10,7 +10,7 @@ import ( gqlclient "github.com/pluralsh/console-client-go" ) -type project struct { +type Project struct { Id types.String `tfsdk:"id"` Name types.String `tfsdk:"name"` Description types.String `tfsdk:"description"` @@ -18,7 +18,7 @@ type project struct { Bindings *common.Bindings `tfsdk:"bindings"` } -func (p *project) Attributes(ctx context.Context, d diag.Diagnostics) (*gqlclient.ProjectAttributes, error) { +func (p *Project) Attributes(ctx context.Context, d diag.Diagnostics) (*gqlclient.ProjectAttributes, error) { return &gqlclient.ProjectAttributes{ Name: p.Name.ValueString(), Description: p.Description.ValueStringPointer(), @@ -27,7 +27,7 @@ func (p *project) Attributes(ctx context.Context, d diag.Diagnostics) (*gqlclien }, nil } -func (p *project) From(project *gqlclient.ProjectFragment, ctx context.Context, d diag.Diagnostics) { +func (p *Project) From(project *gqlclient.ProjectFragment, ctx context.Context, d diag.Diagnostics) { p.Id = types.StringValue(project.ID) p.Name = types.StringValue(project.Name) p.Description = types.StringPointerValue(project.Description) diff --git a/internal/resource/project_schema.go b/internal/resource/project_schema.go deleted file mode 100644 index 149ba78..0000000 --- a/internal/resource/project_schema.go +++ /dev/null @@ -1,80 +0,0 @@ -package resource - -import ( - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" -) - -func (r *ProjectResource) schema() schema.Schema { - return schema.Schema{ - Attributes: map[string]schema.Attribute{ - "id": schema.StringAttribute{ - Description: "Internal identifier of this project.", - MarkdownDescription: "Internal identifier of this project.", - Computed: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, - }, - "name": schema.StringAttribute{ - Description: "Human-readable name of this project.", - MarkdownDescription: "Human-readable name of this project.", - Required: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, - }, - "description": schema.StringAttribute{ - Description: "Description of this project.", - MarkdownDescription: "Description of this project.", - Optional: true, - PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, - }, - "default": schema.StringAttribute{ - Computed: true, - }, - "bindings": schema.SingleNestedAttribute{ - Description: "Read and write policies of this project.", - MarkdownDescription: "Read and write policies of this project.", - Optional: true, - Attributes: map[string]schema.Attribute{ - "read": schema.SetNestedAttribute{ - Description: "Read policies of this project.", - MarkdownDescription: "Read policies of this project.", - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "group_id": schema.StringAttribute{ - Optional: true, - }, - "id": schema.StringAttribute{ - Optional: true, - }, - "user_id": schema.StringAttribute{ - Optional: true, - }, - }, - }, - }, - "write": schema.SetNestedAttribute{ - Description: "Write policies of this project.", - MarkdownDescription: "Write policies of this project.", - Optional: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "group_id": schema.StringAttribute{ - Optional: true, - }, - "id": schema.StringAttribute{ - Optional: true, - }, - "user_id": schema.StringAttribute{ - Optional: true, - }, - }, - }, - }, - }, - PlanModifiers: []planmodifier.Object{objectplanmodifier.UseStateForUnknown()}, - }, - }, - } -} From cc77cdaf2ae526345dec9012bb5b98ddd121421b Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Thu, 13 Jun 2024 09:32:50 +0200 Subject: [PATCH 07/18] format --- internal/provider/provider.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 972aabb..4c6beb3 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -201,8 +201,6 @@ func (p *PluralProvider) DataSources(_ context.Context) []func() datasource.Data func New(version string) func() provider.Provider { return func() provider.Provider { - return &PluralProvider{ - version: version, - } + return &PluralProvider{version: version} } } From e59f25856dac9bd8820c19c418e532aa52bf4e3d Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Thu, 13 Jun 2024 11:07:27 +0200 Subject: [PATCH 08/18] bump client version --- go.mod | 2 +- go.sum | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 7137129..919d7f9 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/mitchellh/go-homedir v1.1.0 - github.com/pluralsh/console-client-go v0.6.0 + github.com/pluralsh/console-client-go v0.7.0 github.com/pluralsh/plural-cli v0.8.5-0.20240216094552-efc34ee6de37 github.com/pluralsh/polly v0.1.7 github.com/samber/lo v1.38.1 diff --git a/go.sum b/go.sum index 509602d..fdb2fbc 100644 --- a/go.sum +++ b/go.sum @@ -856,9 +856,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pluralsh/console-client-go v0.5.16 h1:AHBnis/xUdXbUclNH1GUwcqAsCOzHzOtwetKOIS0dTM= -github.com/pluralsh/console-client-go v0.5.16/go.mod h1:eyCiLA44YbXiYyJh8303jk5JdPkt9McgCo5kBjk4lKo= -github.com/pluralsh/console-client-go v0.6.0/go.mod h1:eyCiLA44YbXiYyJh8303jk5JdPkt9McgCo5kBjk4lKo= +github.com/pluralsh/console-client-go v0.7.0 h1:7BcvftmKhssYd8F06NGsWXKxs7O3K8gQDYrQebvbmHE= +github.com/pluralsh/console-client-go v0.7.0/go.mod h1:eyCiLA44YbXiYyJh8303jk5JdPkt9McgCo5kBjk4lKo= github.com/pluralsh/gqlclient v1.11.0 h1:FfXW7FiEJLHOfTAa7NxDb8jb3aMZNIpCAcG+bg8uHYA= github.com/pluralsh/gqlclient v1.11.0/go.mod h1:qSXKUlio1F2DRPy8el4oFYsmpKbkUYspgPB87T4it5I= github.com/pluralsh/plural-cli v0.8.5-0.20240216094552-efc34ee6de37 h1:DBnaKvKmbTbKwbkrh/2gJBwyHYfaXdxeT3UGh+94K4g= From 048146ffb4a1100bcc48a6c3d29c82984ab0e19f Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Thu, 13 Jun 2024 11:20:38 +0200 Subject: [PATCH 09/18] add example --- example/project/main.tf | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 example/project/main.tf diff --git a/example/project/main.tf b/example/project/main.tf new file mode 100644 index 0000000..41cfd98 --- /dev/null +++ b/example/project/main.tf @@ -0,0 +1,24 @@ +terraform { + required_providers { + plural = { + source = "pluralsh/plural" + version = "0.2.1" + } + } +} + +provider "plural" { + use_cli = true +} + +data "plural_project" "default" { + name = "default" + # id should work as well +} + +data "plural_cluster" "cluster" { + handle = "mgmt" +} + + + From 2010b4269fdee168fd65fcf44d91c6e40fa46f30 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Thu, 13 Jun 2024 11:20:58 +0200 Subject: [PATCH 10/18] add project id to cluster data source --- internal/common/project.go | 14 ++++++++++++++ internal/datasource/cluster.go | 5 +++++ internal/datasource/cluster_model.go | 2 ++ 3 files changed, 21 insertions(+) create mode 100644 internal/common/project.go diff --git a/internal/common/project.go b/internal/common/project.go new file mode 100644 index 0000000..425b6e6 --- /dev/null +++ b/internal/common/project.go @@ -0,0 +1,14 @@ +package common + +import ( + "github.com/hashicorp/terraform-plugin-framework/types" + gqlclient "github.com/pluralsh/console-client-go" +) + +func ProjectFrom(project *gqlclient.TinyProjectFragment) types.String { + if project != nil { + return types.StringValue(project.ID) + } + + return types.StringNull() +} diff --git a/internal/datasource/cluster.go b/internal/datasource/cluster.go index f3f88b6..6df6e59 100644 --- a/internal/datasource/cluster.go +++ b/internal/datasource/cluster.go @@ -46,6 +46,11 @@ func (d *clusterDataSource) Schema(_ context.Context, _ datasource.SchemaRequest MarkdownDescription: "Human-readable name of this cluster, that also translates to cloud resource name.", Computed: true, }, + "project_id": schema.StringAttribute{ + Description: "ID of the project that this cluster belongs to.", + MarkdownDescription: "ID of the project that this cluster belongs to.", + Computed: true, + }, "handle": schema.StringAttribute{ MarkdownDescription: "A short, unique human-readable name used to identify this cluster. Does not necessarily map to the cloud resource name.", Optional: true, diff --git a/internal/datasource/cluster_model.go b/internal/datasource/cluster_model.go index 0374499..5af3567 100644 --- a/internal/datasource/cluster_model.go +++ b/internal/datasource/cluster_model.go @@ -17,6 +17,7 @@ type cluster struct { InsertedAt types.String `tfsdk:"inserted_at"` Name types.String `tfsdk:"name"` Handle types.String `tfsdk:"handle"` + ProjectId types.String `tfsdk:"project_id"` DesiredVersion types.String `tfsdk:"desired_version"` ProviderId types.String `tfsdk:"provider_id"` Cloud types.String `tfsdk:"cloud"` @@ -37,6 +38,7 @@ func (c *cluster) From(cl *console.ClusterFragment, ctx context.Context, d diag. c.InsertedAt = types.StringPointerValue(cl.InsertedAt) c.Name = types.StringValue(cl.Name) c.Handle = types.StringPointerValue(cl.Handle) + c.ProjectId = common.ProjectFrom(cl.Project) c.DesiredVersion = types.StringPointerValue(cl.Version) c.Protect = types.BoolPointerValue(cl.Protect) c.Tags = common.TagsFrom(cl.Tags, c.Tags, d) From 2c6790dcabd538755e67f0353382961c2811a789 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Thu, 13 Jun 2024 11:30:07 +0200 Subject: [PATCH 11/18] fix project data source schema --- example/project/main.tf | 6 +----- internal/datasource/project.go | 11 +++++++++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/example/project/main.tf b/example/project/main.tf index 41cfd98..f16c8d6 100644 --- a/example/project/main.tf +++ b/example/project/main.tf @@ -12,13 +12,9 @@ provider "plural" { } data "plural_project" "default" { - name = "default" - # id should work as well + name = "default" } data "plural_cluster" "cluster" { handle = "mgmt" } - - - diff --git a/internal/datasource/project.go b/internal/datasource/project.go index 0dcfadf..b3afeba 100644 --- a/internal/datasource/project.go +++ b/internal/datasource/project.go @@ -8,8 +8,11 @@ import ( "terraform-provider-plural/internal/common" "terraform-provider-plural/internal/resource" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" ) func NewProjectDataSource() datasource.DataSource { @@ -30,19 +33,23 @@ func (d *projectDataSource) Schema(_ context.Context, _ datasource.SchemaRequest "id": schema.StringAttribute{ Description: "Internal identifier of this project.", MarkdownDescription: "Internal identifier of this project.", + Optional: true, Computed: true, + Validators: []validator.String{stringvalidator.ExactlyOneOf(path.MatchRoot("name"))}, }, "name": schema.StringAttribute{ Description: "Human-readable name of this project.", MarkdownDescription: "Human-readable name of this project.", - Required: true, + Optional: true, + Computed: true, + Validators: []validator.String{stringvalidator.ExactlyOneOf(path.MatchRoot("id"))}, }, "description": schema.StringAttribute{ Description: "Description of this project.", MarkdownDescription: "Description of this project.", Optional: true, }, - "default": schema.StringAttribute{ + "default": schema.BoolAttribute{ Computed: true, }, "bindings": schema.SingleNestedAttribute{ From 8221a9d0e695b9032c9053dfb01ad2f5438a8c0f Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Thu, 13 Jun 2024 11:52:47 +0200 Subject: [PATCH 12/18] add project id to cluster resource --- example/project/main.tf | 36 ++++++++++++++++++++++++++++- internal/resource/cluster_model.go | 8 +++++-- internal/resource/cluster_schema.go | 7 ++++++ 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/example/project/main.tf b/example/project/main.tf index f16c8d6..4ec0bc9 100644 --- a/example/project/main.tf +++ b/example/project/main.tf @@ -15,6 +15,40 @@ data "plural_project" "default" { name = "default" } -data "plural_cluster" "cluster" { +data "plural_cluster" "mgmt" { handle = "mgmt" } + +resource "random_string" "random" { + length = 5 + upper = false + special = false +} + +resource "plural_cluster" "byok" { + name = "byok-${random_string.random.result}" + project_id = data.plural_project.default.id + kubeconfig = { + # Required, can be sourced from environment variables + # export PLURAL_KUBE_CONFIG_PATH to read from local file + } +} + +# data "plural_git_repository" "tf-hello" { +# url = "https://github.com/zreigz/tf-hello.git" +# } + +# resource "plural_infrastructure_stack" "tf-hello" { +# name = "tf-hello-${random_string.random.result}" +# type = "TERRAFORM" +# cluster_id = data.plural_cluster.mgmt.id +# repository = { +# id = data.plural_git_repository.tf-hello.id +# ref = "main" +# folder = "terraform" +# } +# configuration = { +# image = "ghcr.io/pluralsh/harness" +# version = "sha-e9b2089-terraform-1.8" +# } +# } \ No newline at end of file diff --git a/internal/resource/cluster_model.go b/internal/resource/cluster_model.go index ab724c0..2b1a87e 100644 --- a/internal/resource/cluster_model.go +++ b/internal/resource/cluster_model.go @@ -17,6 +17,7 @@ type cluster struct { InsertedAt types.String `tfsdk:"inserted_at"` Name types.String `tfsdk:"name"` Handle types.String `tfsdk:"handle"` + ProjectId types.String `tfsdk:"project_id"` // Version types.String `tfsdk:"version"` // DesiredVersion types.String `tfsdk:"desired_version"` // ProviderId types.String `tfsdk:"provider_id"` @@ -73,8 +74,9 @@ func (c *cluster) TagsAttribute(ctx context.Context, d diag.Diagnostics) []*cons func (c *cluster) Attributes(ctx context.Context, d diag.Diagnostics) console.ClusterAttributes { return console.ClusterAttributes{ - Name: c.Name.ValueString(), - Handle: c.Handle.ValueStringPointer(), + Name: c.Name.ValueString(), + Handle: c.Handle.ValueStringPointer(), + ProjectID: c.ProjectId.ValueStringPointer(), // ProviderID: c.ProviderId.ValueStringPointer(), // Version: c.Version.ValueStringPointer(), Protect: c.Protect.ValueBoolPointer(), @@ -109,6 +111,7 @@ func (c *cluster) From(cl *console.ClusterFragment, ctx context.Context, d diag. c.InsertedAt = types.StringPointerValue(cl.InsertedAt) c.Name = types.StringValue(cl.Name) c.Handle = types.StringPointerValue(cl.Handle) + c.ProjectId = common.ProjectFrom(cl.Project) // c.DesiredVersion = c.ClusterVersionFrom(cl.Provider, cl.Version, cl.CurrentVersion) c.Protect = types.BoolPointerValue(cl.Protect) c.Tags = common.TagsFrom(cl.Tags, c.Tags, d) @@ -122,6 +125,7 @@ func (c *cluster) FromCreate(cc *console.CreateCluster, ctx context.Context, d d c.InsertedAt = types.StringPointerValue(cc.CreateCluster.InsertedAt) c.Name = types.StringValue(cc.CreateCluster.Name) c.Handle = types.StringPointerValue(cc.CreateCluster.Handle) + c.ProjectId = common.ProjectFrom(cc.CreateCluster.Project) // c.DesiredVersion = c.ClusterVersionFrom(cc.CreateCluster.Provider, cc.CreateCluster.Version, cc.CreateCluster.CurrentVersion) c.Protect = types.BoolPointerValue(cc.CreateCluster.Protect) c.Tags = common.TagsFrom(cc.CreateCluster.Tags, c.Tags, d) diff --git a/internal/resource/cluster_schema.go b/internal/resource/cluster_schema.go index f37e457..1585e90 100644 --- a/internal/resource/cluster_schema.go +++ b/internal/resource/cluster_schema.go @@ -46,6 +46,13 @@ func (r *clusterResource) schema() schema.Schema { Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, }, + "project_id": schema.StringAttribute{ + Description: "ID of the project that this cluster belongs to.", + MarkdownDescription: "ID of the project that this cluster belongs to.", + Computed: true, + Optional: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + }, // "version": schema.StringAttribute{ // Description: "Kubernetes version to use for this cluster. Leave empty for bring your own cluster. Supported version ranges can be found at https://github.com/pluralsh/console/tree/master/static/k8s-versions.", // MarkdownDescription: "Kubernetes version to use for this cluster. Leave empty for bring your own cluster. Supported version ranges can be found at https://github.com/pluralsh/console/tree/master/static/k8s-versions.", From 1216f8460ed01d2babcf377705f97b1ba18a16f9 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Thu, 13 Jun 2024 12:58:38 +0200 Subject: [PATCH 13/18] fix project resource schema --- example/project/main.tf | 21 +++++++++++++-------- internal/resource/project.go | 2 +- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/example/project/main.tf b/example/project/main.tf index 4ec0bc9..69717f0 100644 --- a/example/project/main.tf +++ b/example/project/main.tf @@ -25,19 +25,24 @@ resource "random_string" "random" { special = false } -resource "plural_cluster" "byok" { - name = "byok-${random_string.random.result}" - project_id = data.plural_project.default.id - kubeconfig = { - # Required, can be sourced from environment variables - # export PLURAL_KUBE_CONFIG_PATH to read from local file - } +resource "plural_project" "test" { + name = "test-${random_string.random.result}" + description = "test project created by terraform provider" } +# resource "plural_cluster" "byok" { +# name = "byok-${random_string.random.result}" +# project_id = data.plural_project.default.id +# kubeconfig = { +# # Required, can be sourced from environment variables +# # export PLURAL_KUBE_CONFIG_PATH to read from local file +# } +# } +# # data "plural_git_repository" "tf-hello" { # url = "https://github.com/zreigz/tf-hello.git" # } - +# # resource "plural_infrastructure_stack" "tf-hello" { # name = "tf-hello-${random_string.random.result}" # type = "TERRAFORM" diff --git a/internal/resource/project.go b/internal/resource/project.go index be1b128..fd2b7ba 100644 --- a/internal/resource/project.go +++ b/internal/resource/project.go @@ -52,7 +52,7 @@ func (r *ProjectResource) Schema(_ context.Context, _ resource.SchemaRequest, re Optional: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, }, - "default": schema.StringAttribute{ + "default": schema.BoolAttribute{ Computed: true, }, "bindings": schema.SingleNestedAttribute{ From 41f316029ac5ba20e68d023f7de720556182b9c4 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Thu, 13 Jun 2024 13:10:09 +0200 Subject: [PATCH 14/18] fix project resource --- example/project/main.tf | 2 +- internal/resource/project.go | 4 +++- internal/resource/project_model.go | 10 +++++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/example/project/main.tf b/example/project/main.tf index 69717f0..bfc10e4 100644 --- a/example/project/main.tf +++ b/example/project/main.tf @@ -27,7 +27,7 @@ resource "random_string" "random" { resource "plural_project" "test" { name = "test-${random_string.random.result}" - description = "test project created by terraform provider" + description = "test project created by terraform" } # resource "plural_cluster" "byok" { diff --git a/internal/resource/project.go b/internal/resource/project.go index fd2b7ba..fab24b4 100644 --- a/internal/resource/project.go +++ b/internal/resource/project.go @@ -10,6 +10,7 @@ import ( "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/boolplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" @@ -53,7 +54,8 @@ func (r *ProjectResource) Schema(_ context.Context, _ resource.SchemaRequest, re PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, }, "default": schema.BoolAttribute{ - Computed: true, + Computed: true, + PlanModifiers: []planmodifier.Bool{boolplanmodifier.UseStateForUnknown()}, }, "bindings": schema.SingleNestedAttribute{ Description: "Read and write policies of this project.", diff --git a/internal/resource/project_model.go b/internal/resource/project_model.go index 6afd4a7..c30bdd7 100644 --- a/internal/resource/project_model.go +++ b/internal/resource/project_model.go @@ -31,6 +31,14 @@ func (p *Project) From(project *gqlclient.ProjectFragment, ctx context.Context, p.Id = types.StringValue(project.ID) p.Name = types.StringValue(project.Name) p.Description = types.StringPointerValue(project.Description) - p.Default = types.BoolPointerValue(project.Default) + p.Default = defaultFrom(project.Default) p.Bindings.From(project.ReadBindings, project.WriteBindings, ctx, d) } + +func defaultFrom(def *bool) types.Bool { + if def == nil { + return types.BoolValue(false) + } + + return types.BoolValue(*def) +} From 497e8d353c0407ab1ffe31c160e1ffeb66cbe783 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Thu, 13 Jun 2024 13:31:33 +0200 Subject: [PATCH 15/18] add project id to stack resource --- example/project/main.tf | 48 +++++++++---------- .../resource/infrastructure_stack_model.go | 3 ++ .../resource/infrastructure_stack_schema.go | 6 +++ 3 files changed, 33 insertions(+), 24 deletions(-) diff --git a/example/project/main.tf b/example/project/main.tf index bfc10e4..1ce7f6b 100644 --- a/example/project/main.tf +++ b/example/project/main.tf @@ -19,17 +19,21 @@ data "plural_cluster" "mgmt" { handle = "mgmt" } +data "plural_git_repository" "tf-hello" { + url = "https://github.com/zreigz/tf-hello.git" +} + resource "random_string" "random" { length = 5 upper = false special = false } -resource "plural_project" "test" { - name = "test-${random_string.random.result}" - description = "test project created by terraform" -} - +# resource "plural_project" "test" { +# name = "test-${random_string.random.result}" +# description = "test project created by terraform" +# } +# # resource "plural_cluster" "byok" { # name = "byok-${random_string.random.result}" # project_id = data.plural_project.default.id @@ -38,22 +42,18 @@ resource "plural_project" "test" { # # export PLURAL_KUBE_CONFIG_PATH to read from local file # } # } -# -# data "plural_git_repository" "tf-hello" { -# url = "https://github.com/zreigz/tf-hello.git" -# } -# -# resource "plural_infrastructure_stack" "tf-hello" { -# name = "tf-hello-${random_string.random.result}" -# type = "TERRAFORM" -# cluster_id = data.plural_cluster.mgmt.id -# repository = { -# id = data.plural_git_repository.tf-hello.id -# ref = "main" -# folder = "terraform" -# } -# configuration = { -# image = "ghcr.io/pluralsh/harness" -# version = "sha-e9b2089-terraform-1.8" -# } -# } \ No newline at end of file + +resource "plural_infrastructure_stack" "tf-hello" { + name = "tf-hello-${random_string.random.result}" + type = "TERRAFORM" + cluster_id = data.plural_cluster.mgmt.id + repository = { + id = data.plural_git_repository.tf-hello.id + ref = "main" + folder = "terraform" + } + configuration = { + image = "ghcr.io/pluralsh/harness" + version = "sha-e9b2089-terraform-1.8" + } +} \ No newline at end of file diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index 44937f5..be83abc 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -21,6 +21,7 @@ type infrastructureStack struct { Actor types.String `tfsdk:"actor"` Approval types.Bool `tfsdk:"approval"` Detach types.Bool `tfsdk:"detach"` + ProjectId types.String `tfsdk:"project_id"` ClusterId types.String `tfsdk:"cluster_id"` Repository *InfrastructureStackRepository `tfsdk:"repository"` Configuration *InfrastructureStackConfiguration `tfsdk:"configuration"` @@ -35,6 +36,7 @@ func (is *infrastructureStack) Attributes(ctx context.Context, d diag.Diagnostic Name: is.Name.ValueString(), Type: gqlclient.StackType(is.Type.ValueString()), RepositoryID: is.Repository.Id.ValueString(), + ProjectID: is.ProjectId.ValueStringPointer(), ClusterID: is.ClusterId.ValueString(), Git: is.Repository.Attributes(), JobSpec: is.JobSpec.Attributes(ctx, d), @@ -97,6 +99,7 @@ func (is *infrastructureStack) From(stack *gqlclient.InfrastructureStackFragment is.Name = types.StringValue(stack.Name) is.Type = types.StringValue(string(stack.Type)) is.Approval = types.BoolPointerValue(stack.Approval) + is.ProjectId = common.ProjectFrom(stack.Project) is.ClusterId = types.StringValue(stack.Cluster.ID) is.Repository.From(stack.Repository, stack.Git) is.Configuration.From(ctx, stack.Configuration, d) diff --git a/internal/resource/infrastructure_stack_schema.go b/internal/resource/infrastructure_stack_schema.go index 2517b5b..0833824 100644 --- a/internal/resource/infrastructure_stack_schema.go +++ b/internal/resource/infrastructure_stack_schema.go @@ -56,6 +56,12 @@ func (r *InfrastructureStackResource) schema() schema.Schema { MarkdownDescription: "The User email to use for default Plural authentication in this stack.", Optional: true, }, + "project_id": schema.StringAttribute{ + Description: "ID of the project that this stack belongs to.", + MarkdownDescription: "ID of the project that this stack belongs to.", + Computed: true, + Optional: true, + }, "cluster_id": schema.StringAttribute{ Description: "The cluster on which the stack will be applied.", MarkdownDescription: "The cluster on which the stack will be applied.", From 18e5dc9babfe8b4663d35aebdf448db7c348353d Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Fri, 14 Jun 2024 10:47:42 +0200 Subject: [PATCH 16/18] bump client version --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 919d7f9..f24a3fe 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/mitchellh/go-homedir v1.1.0 - github.com/pluralsh/console-client-go v0.7.0 + github.com/pluralsh/console-client-go v0.8.0 github.com/pluralsh/plural-cli v0.8.5-0.20240216094552-efc34ee6de37 github.com/pluralsh/polly v0.1.7 github.com/samber/lo v1.38.1 diff --git a/go.sum b/go.sum index fdb2fbc..4c9c5c2 100644 --- a/go.sum +++ b/go.sum @@ -856,8 +856,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pluralsh/console-client-go v0.7.0 h1:7BcvftmKhssYd8F06NGsWXKxs7O3K8gQDYrQebvbmHE= -github.com/pluralsh/console-client-go v0.7.0/go.mod h1:eyCiLA44YbXiYyJh8303jk5JdPkt9McgCo5kBjk4lKo= +github.com/pluralsh/console-client-go v0.8.0 h1:wCX92bN5EPW8g58neebJPFOm0YQKPCdLBJItSku4dec= +github.com/pluralsh/console-client-go v0.8.0/go.mod h1:eyCiLA44YbXiYyJh8303jk5JdPkt9McgCo5kBjk4lKo= github.com/pluralsh/gqlclient v1.11.0 h1:FfXW7FiEJLHOfTAa7NxDb8jb3aMZNIpCAcG+bg8uHYA= github.com/pluralsh/gqlclient v1.11.0/go.mod h1:qSXKUlio1F2DRPy8el4oFYsmpKbkUYspgPB87T4it5I= github.com/pluralsh/plural-cli v0.8.5-0.20240216094552-efc34ee6de37 h1:DBnaKvKmbTbKwbkrh/2gJBwyHYfaXdxeT3UGh+94K4g= From ec48fe4c62e66cae96608e895dbc2927554cdd62 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Fri, 14 Jun 2024 12:07:25 +0200 Subject: [PATCH 17/18] regenerate docs --- docs/data-sources/cluster.md | 1 + docs/resources/cluster.md | 1 + docs/resources/infrastructure_stack.md | 1 + 3 files changed, 3 insertions(+) diff --git a/docs/data-sources/cluster.md b/docs/data-sources/cluster.md index 1e1e1ee..90a1fcd 100644 --- a/docs/data-sources/cluster.md +++ b/docs/data-sources/cluster.md @@ -28,6 +28,7 @@ A representation of a cluster you can deploy to. - `metadata` (String) Arbitrary JSON metadata to store user-specific state of this cluster (e.g. IAM roles for add-ons). - `name` (String) Human-readable name of this cluster, that also translates to cloud resource name. - `node_pools` (Attributes Map) Map of node pool specs managed by this cluster, where the key is name of the node pool and value contains the spec. (see [below for nested schema](#nestedatt--node_pools)) +- `project_id` (String) ID of the project that this cluster belongs to. - `protect` (Boolean) If set to `true` then this cluster cannot be deleted. - `provider_id` (String) Provider used to create this cluster. - `tags` (Map of String) Key-value tags used to filter clusters. diff --git a/docs/resources/cluster.md b/docs/resources/cluster.md index 8d3d9f5..7f8da7d 100644 --- a/docs/resources/cluster.md +++ b/docs/resources/cluster.md @@ -28,6 +28,7 @@ A representation of a cluster you can deploy to. - `helm_values` (String) Additional Helm values you'd like to use in deployment agent Helm installs. This is useful for BYOK clusters that need to use custom images or other constructs. - `kubeconfig` (Attributes) (see [below for nested schema](#nestedatt--kubeconfig)) - `metadata` (String) Arbitrary JSON metadata to store user-specific state of this cluster (e.g. IAM roles for add-ons). +- `project_id` (String) ID of the project that this cluster belongs to. - `protect` (Boolean) If set to `true` then this cluster cannot be deleted. - `tags` (Map of String) Key-value tags used to filter clusters. diff --git a/docs/resources/infrastructure_stack.md b/docs/resources/infrastructure_stack.md index 8dfe229..270b191 100644 --- a/docs/resources/infrastructure_stack.md +++ b/docs/resources/infrastructure_stack.md @@ -32,6 +32,7 @@ description: |- - `environment` (Attributes Set) Defines environment variables for the stack. (see [below for nested schema](#nestedatt--environment)) - `files` (Map of String) File path-content map. - `job_spec` (Attributes) Repository information used to pull stack. (see [below for nested schema](#nestedatt--job_spec)) +- `project_id` (String) ID of the project that this stack belongs to. ### Read-Only From 7d1b7ee2ba4ee4754de8dac8cc01cbf0a8a9e7c5 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Fri, 14 Jun 2024 12:07:36 +0200 Subject: [PATCH 18/18] regenerate docs --- docs/data-sources/project.md | 54 ++++++++++++++++++++++++++++++++++ docs/resources/project.md | 57 ++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 docs/data-sources/project.md create mode 100644 docs/resources/project.md diff --git a/docs/data-sources/project.md b/docs/data-sources/project.md new file mode 100644 index 0000000..7e0e7bd --- /dev/null +++ b/docs/data-sources/project.md @@ -0,0 +1,54 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "plural_project Data Source - terraform-provider-plural" +subcategory: "" +description: |- + +--- + +# plural_project (Data Source) + + + + + + +## Schema + +### Optional + +- `bindings` (Attributes) Read and write policies of this project. (see [below for nested schema](#nestedatt--bindings)) +- `description` (String) Description of this project. +- `id` (String) Internal identifier of this project. +- `name` (String) Human-readable name of this project. + +### Read-Only + +- `default` (Boolean) + + +### Nested Schema for `bindings` + +Optional: + +- `read` (Attributes Set) Read policies of this project. (see [below for nested schema](#nestedatt--bindings--read)) +- `write` (Attributes Set) Write policies of this project. (see [below for nested schema](#nestedatt--bindings--write)) + + +### Nested Schema for `bindings.read` + +Optional: + +- `group_id` (String) +- `id` (String) +- `user_id` (String) + + + +### Nested Schema for `bindings.write` + +Optional: + +- `group_id` (String) +- `id` (String) +- `user_id` (String) diff --git a/docs/resources/project.md b/docs/resources/project.md new file mode 100644 index 0000000..c27b9d1 --- /dev/null +++ b/docs/resources/project.md @@ -0,0 +1,57 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "plural_project Resource - terraform-provider-plural" +subcategory: "" +description: |- + +--- + +# plural_project (Resource) + + + + + + +## Schema + +### Required + +- `name` (String) Human-readable name of this project. + +### Optional + +- `bindings` (Attributes) Read and write policies of this project. (see [below for nested schema](#nestedatt--bindings)) +- `description` (String) Description of this project. + +### Read-Only + +- `default` (Boolean) +- `id` (String) Internal identifier of this project. + + +### Nested Schema for `bindings` + +Optional: + +- `read` (Attributes Set) Read policies of this project. (see [below for nested schema](#nestedatt--bindings--read)) +- `write` (Attributes Set) Write policies of this project. (see [below for nested schema](#nestedatt--bindings--write)) + + +### Nested Schema for `bindings.read` + +Optional: + +- `group_id` (String) +- `id` (String) +- `user_id` (String) + + + +### Nested Schema for `bindings.write` + +Optional: + +- `group_id` (String) +- `id` (String) +- `user_id` (String)