From 0b81d26f011cfe91f22323e761421c2711ba27ab Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Fri, 19 Apr 2024 13:24:08 +0200 Subject: [PATCH 01/57] start adding model --- .../resource/infrastructure_stack_model.go | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 internal/resource/infrastructure_stack_model.go diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go new file mode 100644 index 0000000..2235a7f --- /dev/null +++ b/internal/resource/infrastructure_stack_model.go @@ -0,0 +1,74 @@ +package resource + +import ( + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/types" + gqlclient "github.com/pluralsh/console-client-go" +) + +type InfrastructureStack struct { + Id types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Type types.String `tfsdk:"type"` + ClusterId types.String `tfsdk:"cluster_id"` + Repository *InfrastructureStackRepository `tfsdk:"repository"` + Approval types.Bool `tfsdk:"protect"` + Configuration *InfrastructureStackConfiguration `tfsdk:"configuration"` + Files []*InfrastructureStackFile `tfsdk:"environment"` + Environemnt []*InfrastructureStackEnvironment `tfsdk:"files"` + Bindings *InfrastructureStackBindings `tfsdk:"bindings"` + // TODO: JobSpec *GateJobAttributes `json:"jobSpec,omitempty"` +} + +type InfrastructureStackRepository struct { + Id types.String `tfsdk:"id"` + Ref types.String `tfsdk:"ref"` + Folder types.String `tfsdk:"folder"` +} + +func (isr *InfrastructureStackRepository) From(repository *gqlclient.GitRepository, ref gqlclient.GitRef) { + if isr == nil { + return + } + + isr.Id = types.StringValue(repository.ID) + isr.Ref = types.StringValue(ref.Ref) + isr.Folder = types.StringValue(ref.Folder) +} + +type InfrastructureStackConfiguration struct { + Image types.String `tfsdk:"image"` + Version types.String `tfsdk:"version"` +} + +type InfrastructureStackEnvironment struct { + Name types.String `tfsdk:"name"` + Value types.String `tfsdk:"value"` + Secret types.Bool `tfsdk:"secret"` +} + +type InfrastructureStackFile struct { + Path types.String `tfsdk:"path"` + Content types.String `tfsdk:"content"` +} + +type InfrastructureStackBindings struct { + Read []*InfrastructureStackPolicyBinding `tfsdk:"read"` + Write []*InfrastructureStackPolicyBinding `tfsdk:"write"` +} + +type InfrastructureStackPolicyBinding struct { + GroupID types.String `tfsdk:"group_id"` + ID types.String `tfsdk:"id"` + UserID types.String `tfsdk:"user_id"` +} + +func (is *InfrastructureStack) FromCreate(stack *gqlclient.InfrastructureStack, d diag.Diagnostics) { + is.Id = types.StringPointerValue(stack.ID) + is.Name = types.StringValue(stack.Name) + is.Type = types.StringValue(string(stack.Type)) + is.ClusterId = types.StringValue(stack.Cluster.ID) + is.Repository.From(stack.Repository, stack.Git) + is.Approval = types.BoolPointerValue(stack.Approval) + // TODO ... +} From 7dfb6d45ba878d43e12cd73b96b9a20e7723ece2 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Fri, 19 Apr 2024 13:39:15 +0200 Subject: [PATCH 02/57] complete model struct --- .../resource/infrastructure_stack_model.go | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index 2235a7f..8254fc5 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -17,7 +17,7 @@ type InfrastructureStack struct { Files []*InfrastructureStackFile `tfsdk:"environment"` Environemnt []*InfrastructureStackEnvironment `tfsdk:"files"` Bindings *InfrastructureStackBindings `tfsdk:"bindings"` - // TODO: JobSpec *GateJobAttributes `json:"jobSpec,omitempty"` + JobSpec *InfrastructureStackJobSpec `tfsdk:"job_spec"` } type InfrastructureStackRepository struct { @@ -41,6 +41,15 @@ type InfrastructureStackConfiguration struct { Version types.String `tfsdk:"version"` } +func (isc *InfrastructureStackConfiguration) From(configuration gqlclient.StackConfiguration) { + if isc == nil { + return + } + + isc.Image = types.StringPointerValue(configuration.Image) + isc.Version = types.StringValue(configuration.Version) +} + type InfrastructureStackEnvironment struct { Name types.String `tfsdk:"name"` Value types.String `tfsdk:"value"` @@ -63,6 +72,27 @@ type InfrastructureStackPolicyBinding struct { UserID types.String `tfsdk:"user_id"` } +type InfrastructureStackJobSpec struct { + Namespace types.String `tfsdk:"namespace"` + Raw types.String `tfsdk:"raw"` + Containers []*InfrastructureStackContainerSpec `tfsdk:"containers"` + Labels types.String `tfsdk:"labels"` + Annotations types.String `tfsdk:"annotations"` + ServiceAccount types.String `tfsdk:"serviceAccount"` +} + +type InfrastructureStackContainerSpec struct { + Image types.String `tfsdk:"image"` + Args types.List `tfsdk:"args"` + Env types.Map `tfsdk:"env"` + EnvFrom []*InfrastructureStackContainerEnvFrom `tfsdk:"envFrom"` +} + +type InfrastructureStackContainerEnvFrom struct { + Secret types.String `tfsdk:"secret"` + ConfigMap types.String `tfsdk:"configMap"` +} + func (is *InfrastructureStack) FromCreate(stack *gqlclient.InfrastructureStack, d diag.Diagnostics) { is.Id = types.StringPointerValue(stack.ID) is.Name = types.StringValue(stack.Name) @@ -70,5 +100,6 @@ func (is *InfrastructureStack) FromCreate(stack *gqlclient.InfrastructureStack, is.ClusterId = types.StringValue(stack.Cluster.ID) is.Repository.From(stack.Repository, stack.Git) is.Approval = types.BoolPointerValue(stack.Approval) + is.Configuration.From(stack.Configuration) // TODO ... } From 029c2b55e3255bde4fe36f600987ba05cf2c901d Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Fri, 19 Apr 2024 14:00:43 +0200 Subject: [PATCH 03/57] start mapping from create --- .../resource/infrastructure_stack_model.go | 48 +++++++++++-------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index 8254fc5..fc9f2b8 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -1,8 +1,10 @@ package resource import ( + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" gqlclient "github.com/pluralsh/console-client-go" ) @@ -10,16 +12,40 @@ type InfrastructureStack struct { Id types.String `tfsdk:"id"` Name types.String `tfsdk:"name"` Type types.String `tfsdk:"type"` + Approval types.Bool `tfsdk:"protect"` ClusterId types.String `tfsdk:"cluster_id"` Repository *InfrastructureStackRepository `tfsdk:"repository"` - Approval types.Bool `tfsdk:"protect"` Configuration *InfrastructureStackConfiguration `tfsdk:"configuration"` - Files []*InfrastructureStackFile `tfsdk:"environment"` - Environemnt []*InfrastructureStackEnvironment `tfsdk:"files"` + Files types.Map `tfsdk:"files"` + Environemnt []*InfrastructureStackEnvironment `tfsdk:"environment"` Bindings *InfrastructureStackBindings `tfsdk:"bindings"` JobSpec *InfrastructureStackJobSpec `tfsdk:"job_spec"` } +func (is *InfrastructureStack) FromCreate(stack *gqlclient.InfrastructureStack, d diag.Diagnostics) { + is.Id = types.StringPointerValue(stack.ID) + is.Name = types.StringValue(stack.Name) + is.Type = types.StringValue(string(stack.Type)) + is.Approval = types.BoolPointerValue(stack.Approval) + is.ClusterId = types.StringValue(stack.Cluster.ID) + is.Repository.From(stack.Repository, stack.Git) + is.Configuration.From(stack.Configuration) + is.Files = toInfrastructureStackFiles(stack.Files, d) + // TODO ... +} + +func toInfrastructureStackFiles(files []*gqlclient.StackFile, d diag.Diagnostics) basetypes.MapValue { + resultMap := make(map[string]attr.Value, len(files)) + for _, file := range files { + resultMap[file.Path] = types.StringValue(file.Content) + } + + result, tagsDiagnostics := types.MapValue(types.StringType, resultMap) + d.Append(tagsDiagnostics...) + + return result +} + type InfrastructureStackRepository struct { Id types.String `tfsdk:"id"` Ref types.String `tfsdk:"ref"` @@ -56,11 +82,6 @@ type InfrastructureStackEnvironment struct { Secret types.Bool `tfsdk:"secret"` } -type InfrastructureStackFile struct { - Path types.String `tfsdk:"path"` - Content types.String `tfsdk:"content"` -} - type InfrastructureStackBindings struct { Read []*InfrastructureStackPolicyBinding `tfsdk:"read"` Write []*InfrastructureStackPolicyBinding `tfsdk:"write"` @@ -92,14 +113,3 @@ type InfrastructureStackContainerEnvFrom struct { Secret types.String `tfsdk:"secret"` ConfigMap types.String `tfsdk:"configMap"` } - -func (is *InfrastructureStack) FromCreate(stack *gqlclient.InfrastructureStack, d diag.Diagnostics) { - is.Id = types.StringPointerValue(stack.ID) - is.Name = types.StringValue(stack.Name) - is.Type = types.StringValue(string(stack.Type)) - is.ClusterId = types.StringValue(stack.Cluster.ID) - is.Repository.From(stack.Repository, stack.Git) - is.Approval = types.BoolPointerValue(stack.Approval) - is.Configuration.From(stack.Configuration) - // TODO ... -} From 9cec9fca265c96f84f66725e3bad797ab6a1d5f6 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Fri, 19 Apr 2024 15:58:46 +0200 Subject: [PATCH 04/57] start adding attributes --- go.mod | 4 +- go.sum | 4 +- internal/common/cluster_bindings.go | 9 +++ .../resource/infrastructure_stack_model.go | 81 ++++++++++++++++--- 4 files changed, 85 insertions(+), 13 deletions(-) diff --git a/go.mod b/go.mod index 60cca6e..631f1a4 100644 --- a/go.mod +++ b/go.mod @@ -12,13 +12,14 @@ 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.1.17 + github.com/pluralsh/console-client-go v0.1.18 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 github.com/sirupsen/logrus v1.9.3 gopkg.in/yaml.v2 v2.4.0 helm.sh/helm/v3 v3.11.2 + k8s.io/api v0.26.4 k8s.io/apimachinery v0.26.4 k8s.io/client-go v0.26.4 sigs.k8s.io/yaml v1.3.0 @@ -335,7 +336,6 @@ require ( gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect honnef.co/go/tools v0.4.6 // indirect - k8s.io/api v0.26.4 // indirect k8s.io/apiextensions-apiserver v0.26.1 // indirect k8s.io/apiserver v0.26.1 // indirect k8s.io/cli-runtime v0.26.1 // indirect diff --git a/go.sum b/go.sum index dcdc0a2..407e373 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.1.17 h1:QMtnWdRvV13/sND/CFjFBUR8nyg3JJgwXReSyM6bK7A= -github.com/pluralsh/console-client-go v0.1.17/go.mod h1:eyCiLA44YbXiYyJh8303jk5JdPkt9McgCo5kBjk4lKo= +github.com/pluralsh/console-client-go v0.1.18 h1:wJMr5LTDlMVZoYAEYNVZcSCkS+SV3xAAuApPLzhSW6g= +github.com/pluralsh/console-client-go v0.1.18/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= diff --git a/internal/common/cluster_bindings.go b/internal/common/cluster_bindings.go index f861197..7b033ca 100644 --- a/internal/common/cluster_bindings.go +++ b/internal/common/cluster_bindings.go @@ -26,6 +26,15 @@ func (cb *ClusterBindings) WriteAttributes() []*console.PolicyBindingAttributes return clusterPolicyBindingAttributes(cb.Write) } +func (cb *ClusterBindings) From(readBindings []*console.PolicyBinding, writeBindings []*console.PolicyBinding) { + if cb == nil { + return + } + + // TODO: WriteBindings + // TODO: ReadBindings +} + type ClusterPolicyBinding struct { GroupID types.String `tfsdk:"group_id"` ID types.String `tfsdk:"id"` diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index fc9f2b8..689c6f0 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -1,6 +1,10 @@ package resource import ( + "context" + + "terraform-provider-plural/internal/common" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/types" @@ -8,7 +12,7 @@ import ( gqlclient "github.com/pluralsh/console-client-go" ) -type InfrastructureStack struct { +type infrastructureStack struct { Id types.String `tfsdk:"id"` Name types.String `tfsdk:"name"` Type types.String `tfsdk:"type"` @@ -17,12 +21,29 @@ type InfrastructureStack struct { Repository *InfrastructureStackRepository `tfsdk:"repository"` Configuration *InfrastructureStackConfiguration `tfsdk:"configuration"` Files types.Map `tfsdk:"files"` - Environemnt []*InfrastructureStackEnvironment `tfsdk:"environment"` - Bindings *InfrastructureStackBindings `tfsdk:"bindings"` + Environment types.Set `tfsdk:"environment"` JobSpec *InfrastructureStackJobSpec `tfsdk:"job_spec"` + Bindings *common.ClusterBindings `tfsdk:"bindings"` +} + +func (is *infrastructureStack) Attributes(ctx context.Context, d diag.Diagnostics) gqlclient.StackAttributes { + return gqlclient.StackAttributes{ + Name: is.Name.ValueString(), + Type: gqlclient.StackType(is.Type.ValueString()), + RepositoryID: is.Repository.Id.ValueString(), + ClusterID: is.ClusterId.ValueString(), + Git: gqlclient.GitRefAttributes{}, + JobSpec: nil, + Configuration: gqlclient.StackConfigurationAttributes{}, + Approval: is.Approval.ValueBoolPointer(), + ReadBindings: is.Bindings.ReadAttributes(), + WriteBindings: is.Bindings.WriteAttributes(), + Files: nil, + Environemnt: nil, + } } -func (is *InfrastructureStack) FromCreate(stack *gqlclient.InfrastructureStack, d diag.Diagnostics) { +func (is *infrastructureStack) From(stack *gqlclient.InfrastructureStack, ctx context.Context, d diag.Diagnostics) { is.Id = types.StringPointerValue(stack.ID) is.Name = types.StringValue(stack.Name) is.Type = types.StringValue(string(stack.Type)) @@ -30,11 +51,13 @@ func (is *InfrastructureStack) FromCreate(stack *gqlclient.InfrastructureStack, is.ClusterId = types.StringValue(stack.Cluster.ID) is.Repository.From(stack.Repository, stack.Git) is.Configuration.From(stack.Configuration) - is.Files = toInfrastructureStackFiles(stack.Files, d) - // TODO ... + is.Files = infrastructureStackFilesFrom(stack.Files, d) + is.Environment = infrastructureStackEnvironmentsFrom(stack.Environment, ctx, d) + is.Bindings.From(stack.ReadBindings, stack.WriteBindings) + is.JobSpec.From(stack.JobSpec) } -func toInfrastructureStackFiles(files []*gqlclient.StackFile, d diag.Diagnostics) basetypes.MapValue { +func infrastructureStackFilesFrom(files []*gqlclient.StackFile, d diag.Diagnostics) basetypes.MapValue { resultMap := make(map[string]attr.Value, len(files)) for _, file := range files { resultMap[file.Path] = types.StringValue(file.Content) @@ -82,6 +105,33 @@ type InfrastructureStackEnvironment struct { Secret types.Bool `tfsdk:"secret"` } +var InfrastructureStackEnvironmentAttrTypes = map[string]attr.Type{ + "name": types.StringType, + "value": types.StringType, + "secret": types.BoolType, +} + +func infrastructureStackEnvironmentsFrom(envs []*gqlclient.StackEnvironment, ctx context.Context, d diag.Diagnostics) types.Set { + if len(envs) == 0 { + return types.SetNull(basetypes.ObjectType{AttrTypes: InfrastructureStackEnvironmentAttrTypes}) + } + + values := make([]attr.Value, len(envs)) + for i, file := range envs { + objValue, diags := types.ObjectValueFrom(ctx, InfrastructureStackEnvironmentAttrTypes, InfrastructureStackEnvironment{ + Name: types.StringValue(file.Name), + Value: types.StringValue(file.Value), + Secret: types.BoolPointerValue(file.Secret), + }) + values[i] = objValue + d.Append(diags...) + } + + setValue, diags := types.SetValue(basetypes.ObjectType{AttrTypes: InfrastructureStackEnvironmentAttrTypes}, values) + d.Append(diags...) + return setValue +} + type InfrastructureStackBindings struct { Read []*InfrastructureStackPolicyBinding `tfsdk:"read"` Write []*InfrastructureStackPolicyBinding `tfsdk:"write"` @@ -97,11 +147,24 @@ type InfrastructureStackJobSpec struct { Namespace types.String `tfsdk:"namespace"` Raw types.String `tfsdk:"raw"` Containers []*InfrastructureStackContainerSpec `tfsdk:"containers"` - Labels types.String `tfsdk:"labels"` - Annotations types.String `tfsdk:"annotations"` + Labels types.Map `tfsdk:"labels"` + Annotations types.Map `tfsdk:"annotations"` ServiceAccount types.String `tfsdk:"serviceAccount"` } +func (isjs *InfrastructureStackJobSpec) From(spec *gqlclient.JobGateSpec) { + if isjs == nil { + return + } + + isjs.Namespace = types.StringValue(spec.Namespace) + isjs.Raw = types.StringPointerValue(spec.Raw) + // TODO: Containers + // TODO: Labels + // TODO: Annotations + isjs.ServiceAccount = types.StringPointerValue(spec.ServiceAccount) +} + type InfrastructureStackContainerSpec struct { Image types.String `tfsdk:"image"` Args types.List `tfsdk:"args"` From e02339b5c818680b2d3527e5c3a2930e5df66af6 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Fri, 19 Apr 2024 16:31:15 +0200 Subject: [PATCH 05/57] refactor bidings --- internal/common/cluster_bindings.go | 93 ++++++++++++++----- internal/resource/cluster_model.go | 4 +- .../resource/infrastructure_stack_model.go | 6 +- internal/resource/rbac.go | 4 +- internal/resource/rbac_model.go | 9 +- 5 files changed, 85 insertions(+), 31 deletions(-) diff --git a/internal/common/cluster_bindings.go b/internal/common/cluster_bindings.go index 7b033ca..18d3a73 100644 --- a/internal/common/cluster_bindings.go +++ b/internal/common/cluster_bindings.go @@ -1,38 +1,92 @@ package common import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" console "github.com/pluralsh/console-client-go" ) type ClusterBindings struct { - Read []*ClusterPolicyBinding `tfsdk:"read"` - Write []*ClusterPolicyBinding `tfsdk:"write"` + Read types.Set `tfsdk:"read"` + Write types.Set `tfsdk:"write"` } -func (cb *ClusterBindings) ReadAttributes() []*console.PolicyBindingAttributes { +func (cb *ClusterBindings) ReadAttributes(ctx context.Context, d diag.Diagnostics) []*console.PolicyBindingAttributes { if cb == nil { return []*console.PolicyBindingAttributes{} } - return clusterPolicyBindingAttributes(cb.Read) + return clusterPolicyBindingAttributes(cb.Read, ctx, d) } -func (cb *ClusterBindings) WriteAttributes() []*console.PolicyBindingAttributes { +func (cb *ClusterBindings) WriteAttributes(ctx context.Context, d diag.Diagnostics) []*console.PolicyBindingAttributes { if cb == nil { return []*console.PolicyBindingAttributes{} } - return clusterPolicyBindingAttributes(cb.Write) + return clusterPolicyBindingAttributes(cb.Write, ctx, d) +} + +func clusterPolicyBindingAttributes(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())) + d.Append(bindings.ElementsAs(ctx, &elements, false)...) + + for _, binding := range elements { + result = append(result, &console.PolicyBindingAttributes{ + ID: binding.ID.ValueStringPointer(), + UserID: binding.UserID.ValueStringPointer(), + GroupID: binding.GroupID.ValueStringPointer(), + }) + } + + return result } -func (cb *ClusterBindings) From(readBindings []*console.PolicyBinding, writeBindings []*console.PolicyBinding) { +func (cb *ClusterBindings) From(readBindings []*console.PolicyBinding, writeBindings []*console.PolicyBinding, ctx context.Context, d diag.Diagnostics) { if cb == nil { return } - // TODO: WriteBindings - // TODO: ReadBindings + cb.Read = clusterBindingsFrom(readBindings, ctx, d) + cb.Write = clusterBindingsFrom(writeBindings, ctx, d) +} + +func clusterBindingsFrom(bindings []*console.PolicyBinding, ctx context.Context, d diag.Diagnostics) types.Set { + if len(bindings) == 0 { + return types.SetNull(basetypes.ObjectType{AttrTypes: ClusterPolicyBindingAttrTypes}) + } + + values := make([]attr.Value, len(bindings)) + for i, binding := range bindings { + value := ClusterPolicyBinding{ + ID: types.StringPointerValue(binding.ID), + } + + if binding.User != nil { + value.UserID = types.StringValue(binding.User.ID) + } + + if binding.Group != nil { + value.GroupID = types.StringValue(binding.Group.ID) + } + + objValue, diags := types.ObjectValueFrom(ctx, ClusterPolicyBindingAttrTypes, value) + values[i] = objValue + d.Append(diags...) + } + + setValue, diags := types.SetValue(basetypes.ObjectType{AttrTypes: ClusterPolicyBindingAttrTypes}, values) + d.Append(diags...) + return setValue } type ClusterPolicyBinding struct { @@ -41,19 +95,16 @@ type ClusterPolicyBinding struct { UserID types.String `tfsdk:"user_id"` } -func (c *ClusterPolicyBinding) Attributes() *console.PolicyBindingAttributes { - return &console.PolicyBindingAttributes{ - ID: c.ID.ValueStringPointer(), - UserID: c.UserID.ValueStringPointer(), - GroupID: c.GroupID.ValueStringPointer(), - } +var ClusterPolicyBindingAttrTypes = map[string]attr.Type{ + "group_id": types.StringType, + "id": types.StringType, + "user_id": types.StringType, } -func clusterPolicyBindingAttributes(bindings []*ClusterPolicyBinding) []*console.PolicyBindingAttributes { - result := make([]*console.PolicyBindingAttributes, len(bindings)) - for i, b := range bindings { - result[i] = b.Attributes() +func (cpb *ClusterPolicyBinding) Attributes() *console.PolicyBindingAttributes { + return &console.PolicyBindingAttributes{ + ID: cpb.ID.ValueStringPointer(), + UserID: cpb.UserID.ValueStringPointer(), + GroupID: cpb.GroupID.ValueStringPointer(), } - - return result } diff --git a/internal/resource/cluster_model.go b/internal/resource/cluster_model.go index 4eff890..716d09b 100644 --- a/internal/resource/cluster_model.go +++ b/internal/resource/cluster_model.go @@ -76,8 +76,8 @@ func (c *cluster) Attributes(ctx context.Context, d diag.Diagnostics) console.Cl Version: c.Version.ValueStringPointer(), Protect: c.Protect.ValueBoolPointer(), CloudSettings: c.CloudSettings.Attributes(), - ReadBindings: c.Bindings.ReadAttributes(), - WriteBindings: c.Bindings.WriteAttributes(), + ReadBindings: c.Bindings.ReadAttributes(ctx, d), + WriteBindings: c.Bindings.WriteAttributes(ctx, d), Tags: c.TagsAttribute(ctx, d), NodePools: c.NodePoolsAttribute(ctx, d), Metadata: c.Metadata.ValueStringPointer(), diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index 689c6f0..2462cb5 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -36,8 +36,8 @@ func (is *infrastructureStack) Attributes(ctx context.Context, d diag.Diagnostic JobSpec: nil, Configuration: gqlclient.StackConfigurationAttributes{}, Approval: is.Approval.ValueBoolPointer(), - ReadBindings: is.Bindings.ReadAttributes(), - WriteBindings: is.Bindings.WriteAttributes(), + ReadBindings: is.Bindings.ReadAttributes(ctx, d), + WriteBindings: is.Bindings.WriteAttributes(ctx, d), Files: nil, Environemnt: nil, } @@ -53,7 +53,7 @@ func (is *infrastructureStack) From(stack *gqlclient.InfrastructureStack, ctx co is.Configuration.From(stack.Configuration) is.Files = infrastructureStackFilesFrom(stack.Files, d) is.Environment = infrastructureStackEnvironmentsFrom(stack.Environment, ctx, d) - is.Bindings.From(stack.ReadBindings, stack.WriteBindings) + is.Bindings.From(stack.ReadBindings, stack.WriteBindings, ctx, d) is.JobSpec.From(stack.JobSpec) } diff --git a/internal/resource/rbac.go b/internal/resource/rbac.go index c432633..4d8a0a6 100644 --- a/internal/resource/rbac.go +++ b/internal/resource/rbac.go @@ -126,7 +126,7 @@ func (r *rbacResource) Create(ctx context.Context, req resource.CreateRequest, r return } - _, err := r.client.UpdateRbac(ctx, data.Attributes(), data.ServiceId.ValueStringPointer(), data.ClusterId.ValueStringPointer(), nil) + _, err := r.client.UpdateRbac(ctx, data.Attributes(ctx, resp.Diagnostics), data.ServiceId.ValueStringPointer(), data.ClusterId.ValueStringPointer(), nil) if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update rbac, got error: %s", err)) return @@ -146,7 +146,7 @@ func (r *rbacResource) Update(ctx context.Context, req resource.UpdateRequest, r return } - _, err := r.client.UpdateRbac(ctx, data.Attributes(), data.ServiceId.ValueStringPointer(), data.ClusterId.ValueStringPointer(), nil) + _, err := r.client.UpdateRbac(ctx, data.Attributes(ctx, resp.Diagnostics), data.ServiceId.ValueStringPointer(), data.ClusterId.ValueStringPointer(), nil) if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update rbac, got error: %s", err)) return diff --git a/internal/resource/rbac_model.go b/internal/resource/rbac_model.go index 3f10ce0..b89b4bb 100644 --- a/internal/resource/rbac_model.go +++ b/internal/resource/rbac_model.go @@ -1,8 +1,11 @@ package resource import ( + "context" + "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" ) @@ -13,9 +16,9 @@ type rbac struct { Bindings *common.ClusterBindings `tfsdk:"rbac"` } -func (g *rbac) Attributes() gqlclient.RbacAttributes { +func (g *rbac) Attributes(ctx context.Context, d diag.Diagnostics) gqlclient.RbacAttributes { return gqlclient.RbacAttributes{ - ReadBindings: g.Bindings.ReadAttributes(), - WriteBindings: g.Bindings.WriteAttributes(), + ReadBindings: g.Bindings.ReadAttributes(ctx, d), + WriteBindings: g.Bindings.WriteAttributes(ctx, d), } } From b40ffe8086cc02951a89166fc4647a166630a3f8 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Fri, 19 Apr 2024 16:51:55 +0200 Subject: [PATCH 06/57] start adding resource --- internal/resource/infrastructure_stack.go | 134 ++++++++++++++++++ .../resource/infrastructure_stack_model.go | 10 +- .../resource/infrastructure_stack_schema.go | 36 +++++ 3 files changed, 175 insertions(+), 5 deletions(-) create mode 100644 internal/resource/infrastructure_stack.go create mode 100644 internal/resource/infrastructure_stack_schema.go diff --git a/internal/resource/infrastructure_stack.go b/internal/resource/infrastructure_stack.go new file mode 100644 index 0000000..88e41bb --- /dev/null +++ b/internal/resource/infrastructure_stack.go @@ -0,0 +1,134 @@ +package resource + +import ( + "context" + "fmt" + "time" + + "terraform-provider-plural/internal/common" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "k8s.io/apimachinery/pkg/util/wait" + + "terraform-provider-plural/internal/client" +) + +var _ resource.Resource = &InfrastructureStackResource{} +var _ resource.ResourceWithImportState = &InfrastructureStackResource{} + +func NewInfrastructureStackResource() resource.Resource { + return &InfrastructureStackResource{} +} + +// InfrastructureStackResource defines the infrastructure stack resource implementation. +type InfrastructureStackResource struct { + client *client.Client +} + +func (r *InfrastructureStackResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_infrastructure_stack" +} + +func (r *InfrastructureStackResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = r.schema() +} + +func (r *InfrastructureStackResource) 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 Infrastructure Stack 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 *InfrastructureStackResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + data := new(infrastructureStack) + resp.Diagnostics.Append(req.Plan.Get(ctx, data)...) + if resp.Diagnostics.HasError() { + return + } + + sd, err := r.client.CreateStack(ctx, data.Attributes(ctx, resp.Diagnostics)) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create infrastructure stack, got error: %s", err)) + return + } + + data.From(sd.CreateStack, ctx, resp.Diagnostics) + resp.Diagnostics.Append(resp.State.Set(ctx, data)...) +} + +func (r *InfrastructureStackResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + data := new(infrastructureStack) + resp.Diagnostics.Append(req.State.Get(ctx, data)...) + if resp.Diagnostics.HasError() { + return + } + + response, err := r.client.GetInfrastructureStack(ctx, data.Id.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read infrastructure stack, got error: %s", err)) + return + } + + data.From(response.InfrastructureStack, ctx, resp.Diagnostics) + resp.Diagnostics.Append(resp.State.Set(ctx, data)...) +} + +func (r *InfrastructureStackResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + data := new(infrastructureStack) + resp.Diagnostics.Append(req.Plan.Get(ctx, data)...) + if resp.Diagnostics.HasError() { + return + } + + _, err := r.client.UpdateStack(ctx, data.Id.ValueString(), data.Attributes(ctx, resp.Diagnostics)) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update infrastructure stack, got error: %s", err)) + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, data)...) +} + +func (r *InfrastructureStackResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + data := new(infrastructureStack) + resp.Diagnostics.Append(req.State.Get(ctx, data)...) + if resp.Diagnostics.HasError() { + return + } + + _, err := r.client.DeleteStack(ctx, data.Id.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete infrastructure stack, got error: %s", err)) + return + } + + err = wait.WaitForWithContext(ctx, client.Ticker(5*time.Second), func(ctx context.Context) (bool, error) { + _, err := r.client.GetInfrastructureStack(ctx, data.Id.ValueString()) + if client.IsNotFound(err) { + return true, nil + } + + return false, err + }) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Error during watiting for infrastructure stack to be deleted, got error: %s", err)) + return + } +} + +func (r *InfrastructureStackResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index 2462cb5..60a1845 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -43,7 +43,7 @@ func (is *infrastructureStack) Attributes(ctx context.Context, d diag.Diagnostic } } -func (is *infrastructureStack) From(stack *gqlclient.InfrastructureStack, ctx context.Context, d diag.Diagnostics) { +func (is *infrastructureStack) From(stack *gqlclient.InfrastructureStackFragment, ctx context.Context, d diag.Diagnostics) { is.Id = types.StringPointerValue(stack.ID) is.Name = types.StringValue(stack.Name) is.Type = types.StringValue(string(stack.Type)) @@ -57,7 +57,7 @@ func (is *infrastructureStack) From(stack *gqlclient.InfrastructureStack, ctx co is.JobSpec.From(stack.JobSpec) } -func infrastructureStackFilesFrom(files []*gqlclient.StackFile, d diag.Diagnostics) basetypes.MapValue { +func infrastructureStackFilesFrom(files []*gqlclient.StackFileFragment, d diag.Diagnostics) basetypes.MapValue { resultMap := make(map[string]attr.Value, len(files)) for _, file := range files { resultMap[file.Path] = types.StringValue(file.Content) @@ -75,7 +75,7 @@ type InfrastructureStackRepository struct { Folder types.String `tfsdk:"folder"` } -func (isr *InfrastructureStackRepository) From(repository *gqlclient.GitRepository, ref gqlclient.GitRef) { +func (isr *InfrastructureStackRepository) From(repository *gqlclient.GitRepositoryFragment, ref gqlclient.GitRefFragment) { if isr == nil { return } @@ -90,7 +90,7 @@ type InfrastructureStackConfiguration struct { Version types.String `tfsdk:"version"` } -func (isc *InfrastructureStackConfiguration) From(configuration gqlclient.StackConfiguration) { +func (isc *InfrastructureStackConfiguration) From(configuration gqlclient.StackConfigurationFragment) { if isc == nil { return } @@ -152,7 +152,7 @@ type InfrastructureStackJobSpec struct { ServiceAccount types.String `tfsdk:"serviceAccount"` } -func (isjs *InfrastructureStackJobSpec) From(spec *gqlclient.JobGateSpec) { +func (isjs *InfrastructureStackJobSpec) From(spec *gqlclient.JobGateSpecFragment) { if isjs == nil { return } diff --git a/internal/resource/infrastructure_stack_schema.go b/internal/resource/infrastructure_stack_schema.go new file mode 100644 index 0000000..f55bbf4 --- /dev/null +++ b/internal/resource/infrastructure_stack_schema.go @@ -0,0 +1,36 @@ +package resource + +import ( + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "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/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + gqlclient "github.com/pluralsh/console-client-go" +) + +func (r *InfrastructureStackResource) schema() schema.Schema { + return schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + Description: "Internal identifier of this infrastructure stack.", + MarkdownDescription: "Internal identifier of this infrastructure stack.", + PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, + }, + "name": schema.StringAttribute{ + Required: true, + Description: "Human-readable name of this infrastructure stack.", + MarkdownDescription: "Human-readable name of this infrastructure stack.", + PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, + }, + "type": schema.StringAttribute{ + Required: true, + Description: "A type for the stack, specifies the tool to use to apply it.", + MarkdownDescription: "A type for the stack, specifies the tool to use to apply it.", + PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, + Validators: []validator.String{stringvalidator.OneOf(string(gqlclient.StackTypeAnsible), string(gqlclient.StackTypeTerraform))}, + }, + }, + } +} From c29636d7b7010304d95ada097e5ab0fc3509d700 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Fri, 19 Apr 2024 16:54:40 +0200 Subject: [PATCH 07/57] update schema --- .../resource/infrastructure_stack_schema.go | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/internal/resource/infrastructure_stack_schema.go b/internal/resource/infrastructure_stack_schema.go index f55bbf4..eafdd9b 100644 --- a/internal/resource/infrastructure_stack_schema.go +++ b/internal/resource/infrastructure_stack_schema.go @@ -3,6 +3,7 @@ package resource import ( "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" @@ -13,24 +14,30 @@ func (r *InfrastructureStackResource) schema() schema.Schema { return schema.Schema{ Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ + Description: "Internal identifier of this stack.", + MarkdownDescription: "Internal identifier of this stack.", Computed: true, - Description: "Internal identifier of this infrastructure stack.", - MarkdownDescription: "Internal identifier of this infrastructure stack.", PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, }, "name": schema.StringAttribute{ + Description: "Human-readable name of this stack.", + MarkdownDescription: "Human-readable name of this stack.", Required: true, - Description: "Human-readable name of this infrastructure stack.", - MarkdownDescription: "Human-readable name of this infrastructure stack.", PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, }, "type": schema.StringAttribute{ - Required: true, Description: "A type for the stack, specifies the tool to use to apply it.", MarkdownDescription: "A type for the stack, specifies the tool to use to apply it.", + Required: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, Validators: []validator.String{stringvalidator.OneOf(string(gqlclient.StackTypeAnsible), string(gqlclient.StackTypeTerraform))}, }, + "approval": schema.BoolAttribute{ + Description: "Determines whether to require approval.", + MarkdownDescription: "Determines whether to require approval.", + Optional: true, + Default: booldefault.StaticBool(false), + }, }, } } From d01e0f5a7167d75b9fe0f27b1d5680d3807cd446 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 22 Apr 2024 10:22:08 +0200 Subject: [PATCH 08/57] update schema --- .../resource/infrastructure_stack_schema.go | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/internal/resource/infrastructure_stack_schema.go b/internal/resource/infrastructure_stack_schema.go index eafdd9b..a73374c 100644 --- a/internal/resource/infrastructure_stack_schema.go +++ b/internal/resource/infrastructure_stack_schema.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" gqlclient "github.com/pluralsh/console-client-go" ) @@ -38,6 +39,81 @@ func (r *InfrastructureStackResource) schema() schema.Schema { Optional: true, Default: booldefault.StaticBool(false), }, + "cluster_id": schema.StringAttribute{ + Description: "The cluster on which the stack will be applied.", + MarkdownDescription: "The cluster on which the stack will be applied.", + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, + }, + "repository": schema.SingleNestedAttribute{ + Description: "Repository information used to pull stack.", + MarkdownDescription: "Repository information used to pull stack.", + Required: true, + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Description: "ID of the repository to pull from.", + MarkdownDescription: "ID of the repository to pull from.", + Required: true, + }, + "ref": schema.StringAttribute{ + Description: "A general git ref, either a branch name or commit sha understandable by `git checkout `.", + MarkdownDescription: "A general git ref, either a branch name or commit sha understandable by \"git checkout \".", + Required: true, + }, + "folder": schema.StringAttribute{ + Description: "The folder where manifests live.", + MarkdownDescription: "The folder where manifests live.", + Required: true, + }, + }, + }, + "configuration": schema.SingleNestedAttribute{ + Description: "Stack configuration.", + MarkdownDescription: "Stack configuration.", + Required: true, + Attributes: map[string]schema.Attribute{ + "image": schema.StringAttribute{ + Description: "Optional custom image you might want to use.", + MarkdownDescription: "Optional custom image you might want to use.", + Optional: true, + }, + "version": schema.StringAttribute{ + Description: "The semver of the tool you wish to use.", + MarkdownDescription: "The semver of the tool you wish to use.", + Required: true, + }, + }, + }, + "files": schema.MapAttribute{ + MarkdownDescription: "File path-content map.", + Optional: true, + ElementType: types.StringType, + }, + "environment": schema.SetNestedAttribute{ + Description: "Defines environment variables for the stack.", + MarkdownDescription: "Defines environment variables for the stack.", + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Description: "Environment variable name.", + MarkdownDescription: "Environment variable name.", + Required: true, + }, + "value": schema.StringAttribute{ + Description: "Environment variable value.", + MarkdownDescription: "Environment variable value.", + Required: true, + }, + "secret": schema.BoolAttribute{ + Description: "Indicates if environment variable is secret.", + MarkdownDescription: "Indicates if environment variable is secret.", + Optional: true, + Default: booldefault.StaticBool(false), + }, + }, + }, + }, }, } } From b3f8556be2b39330f605e1c42e5e45111be1cc14 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 22 Apr 2024 11:49:55 +0200 Subject: [PATCH 09/57] update client --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 631f1a4..cf5c16a 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.1.18 + github.com/pluralsh/console-client-go v0.2.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 407e373..95016e2 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.1.18 h1:wJMr5LTDlMVZoYAEYNVZcSCkS+SV3xAAuApPLzhSW6g= -github.com/pluralsh/console-client-go v0.1.18/go.mod h1:eyCiLA44YbXiYyJh8303jk5JdPkt9McgCo5kBjk4lKo= +github.com/pluralsh/console-client-go v0.2.0 h1:60BPwsyZ9uwAUcFUxGyXRX8C+Pk/W5Dvvvk3R64m0UI= +github.com/pluralsh/console-client-go v0.2.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 f13090381e24caefabc924cf092cfc52f577420d Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 22 Apr 2024 11:50:45 +0200 Subject: [PATCH 10/57] finish schema --- internal/resource/cluster_schema.go | 28 ++-- .../resource/infrastructure_stack_model.go | 22 +-- .../resource/infrastructure_stack_schema.go | 127 ++++++++++++++++++ 3 files changed, 146 insertions(+), 31 deletions(-) diff --git a/internal/resource/cluster_schema.go b/internal/resource/cluster_schema.go index dae73df..50560c1 100644 --- a/internal/resource/cluster_schema.go +++ b/internal/resource/cluster_schema.go @@ -235,19 +235,13 @@ func (r *clusterResource) schema() schema.Schema { NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "group_id": schema.StringAttribute{ - Description: "", - MarkdownDescription: "", - Optional: true, + Optional: true, }, "id": schema.StringAttribute{ - Description: "", - MarkdownDescription: "", - Optional: true, + Optional: true, }, "user_id": schema.StringAttribute{ - Description: "", - MarkdownDescription: "", - Optional: true, + Optional: true, }, }, }, @@ -259,19 +253,13 @@ func (r *clusterResource) schema() schema.Schema { NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "group_id": schema.StringAttribute{ - Description: "", - MarkdownDescription: "", - Optional: true, + Optional: true, }, "id": schema.StringAttribute{ - Description: "", - MarkdownDescription: "", - Optional: true, + Optional: true, }, "user_id": schema.StringAttribute{ - Description: "", - MarkdownDescription: "", - Optional: true, + Optional: true, }, }, }, @@ -477,8 +465,8 @@ func (r *clusterResource) kubeconfigSchema(deprecated bool) schema.SingleNestedA ElementType: types.StringType, }, "env": schema.MapAttribute{ - Description: "Defines environment variables to expose to the process.", - MarkdownDescription: "Defines environment variables to expose to the process.", + Description: "Defines environment variables to expose to the process.", + MarkdownDescription: "Defines environment variables to expose to the process.", Optional: true, ElementType: types.StringType, }, diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index 60a1845..8a99a2b 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -144,12 +144,12 @@ type InfrastructureStackPolicyBinding struct { } type InfrastructureStackJobSpec struct { - Namespace types.String `tfsdk:"namespace"` - Raw types.String `tfsdk:"raw"` - Containers []*InfrastructureStackContainerSpec `tfsdk:"containers"` - Labels types.Map `tfsdk:"labels"` - Annotations types.Map `tfsdk:"annotations"` - ServiceAccount types.String `tfsdk:"serviceAccount"` + Namespace types.String `tfsdk:"namespace"` + Raw types.String `tfsdk:"raw"` + Containers types.Set `tfsdk:"containers"` + Labels types.Map `tfsdk:"labels"` + Annotations types.Map `tfsdk:"annotations"` + ServiceAccount types.String `tfsdk:"service_account"` } func (isjs *InfrastructureStackJobSpec) From(spec *gqlclient.JobGateSpecFragment) { @@ -166,13 +166,13 @@ func (isjs *InfrastructureStackJobSpec) From(spec *gqlclient.JobGateSpecFragment } type InfrastructureStackContainerSpec struct { - Image types.String `tfsdk:"image"` - Args types.List `tfsdk:"args"` - Env types.Map `tfsdk:"env"` - EnvFrom []*InfrastructureStackContainerEnvFrom `tfsdk:"envFrom"` + Image types.String `tfsdk:"image"` + Args types.Set `tfsdk:"args"` + Env types.Map `tfsdk:"env"` + EnvFrom types.Set `tfsdk:"env_from"` } type InfrastructureStackContainerEnvFrom struct { Secret types.String `tfsdk:"secret"` - ConfigMap types.String `tfsdk:"configMap"` + ConfigMap types.String `tfsdk:"config_map"` } diff --git a/internal/resource/infrastructure_stack_schema.go b/internal/resource/infrastructure_stack_schema.go index a73374c..2241452 100644 --- a/internal/resource/infrastructure_stack_schema.go +++ b/internal/resource/infrastructure_stack_schema.go @@ -1,7 +1,10 @@ package resource import ( + "github.com/hashicorp/terraform-plugin-framework-validators/mapvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" @@ -114,6 +117,130 @@ func (r *InfrastructureStackResource) schema() schema.Schema { }, }, }, + "job_spec": schema.SingleNestedAttribute{ + Description: "Repository information used to pull stack.", + MarkdownDescription: "Repository information used to pull stack.", + Optional: true, + Attributes: map[string]schema.Attribute{ + "namespace": schema.StringAttribute{ + Description: "Namespace where job will be deployed.", + MarkdownDescription: "Namespace where job will be deployed.", + Required: true, + }, + "raw": schema.StringAttribute{ + Description: "If you'd rather define the job spec via straight Kubernetes YAML.", + MarkdownDescription: "If you'd rather define the job spec via straight Kubernetes YAML.", + Optional: true, + Validators: []validator.String{ + stringvalidator.ExactlyOneOf( + path.MatchRelative().AtParent().AtName("labels"), + path.MatchRelative().AtParent().AtName("annotations"), + path.MatchRelative().AtParent().AtName("service_account"), + path.MatchRelative().AtParent().AtName("containers"), + ), + }, + }, + "labels": schema.MapAttribute{ + Description: "Kubernetes labels applied to the job.", + MarkdownDescription: "Kubernetes labels applied to the job.", + ElementType: types.StringType, + Optional: true, + Validators: []validator.Map{mapvalidator.ExactlyOneOf(path.MatchRelative().AtParent().AtName("raw"))}, + }, + "annotations": schema.MapAttribute{ + Description: "Kubernetes annotations applied to the job.", + MarkdownDescription: "Kubernetes annotations applied to the job.", + ElementType: types.StringType, + Optional: true, + Validators: []validator.Map{mapvalidator.ExactlyOneOf(path.MatchRelative().AtParent().AtName("raw"))}, + }, + "service_account": schema.StringAttribute{ + Description: "Kubernetes service account for this job.", + MarkdownDescription: "Kubernetes service account for this job.", + Optional: true, + Validators: []validator.String{stringvalidator.ExactlyOneOf(path.MatchRelative().AtParent().AtName("raw"))}, + }, + "containers": schema.SetNestedAttribute{ + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "image": schema.StringAttribute{ + Required: true, + }, + "args": schema.SetAttribute{ + Description: "Arguments to pass to the command when executing it.", + MarkdownDescription: "Arguments to pass to the command when executing it.", + Optional: true, + ElementType: types.StringType, + }, + "env": schema.MapAttribute{ + Description: "Defines environment variables to expose to the process.", + MarkdownDescription: "Defines environment variables to expose to the process.", + Optional: true, + ElementType: types.StringType, + }, + "env_from": schema.SetNestedAttribute{ + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "secret": schema.StringAttribute{ + Required: true, + }, + "config_map": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + }, + }, + Validators: []validator.Set{setvalidator.ExactlyOneOf(path.MatchRelative().AtParent().AtName("raw"))}, + }, + }, + }, + "bindings": schema.SingleNestedAttribute{ + Description: "Read and write policies of this stack.", + MarkdownDescription: "Read and write policies of this stack.", + Optional: true, + Attributes: map[string]schema.Attribute{ + "read": schema.SetNestedAttribute{ + Description: "Read policies of this stack.", + MarkdownDescription: "Read policies of this stack.", + 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 stack.", + MarkdownDescription: "Write policies of this stack.", + 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, + }, + }, + }, + }, + }, + }, }, } } From ffb948c8a3250d4aeec890577be2f4ba23cd575d Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 22 Apr 2024 12:10:48 +0200 Subject: [PATCH 11/57] add example --- example/{ => cluster}/aws/main.tf | 0 example/{ => cluster}/azure/main.tf | 0 example/{ => cluster}/byok/main.tf | 0 example/{ => cluster}/gcp/main.tf | 0 example/cluster_data/main.tf | 16 ------ example/stack/main.tf | 57 +++++++++++++++++++ internal/provider/provider.go | 1 + .../resource/infrastructure_stack_schema.go | 6 +- 8 files changed, 62 insertions(+), 18 deletions(-) rename example/{ => cluster}/aws/main.tf (100%) rename example/{ => cluster}/azure/main.tf (100%) rename example/{ => cluster}/byok/main.tf (100%) rename example/{ => cluster}/gcp/main.tf (100%) delete mode 100644 example/cluster_data/main.tf create mode 100644 example/stack/main.tf diff --git a/example/aws/main.tf b/example/cluster/aws/main.tf similarity index 100% rename from example/aws/main.tf rename to example/cluster/aws/main.tf diff --git a/example/azure/main.tf b/example/cluster/azure/main.tf similarity index 100% rename from example/azure/main.tf rename to example/cluster/azure/main.tf diff --git a/example/byok/main.tf b/example/cluster/byok/main.tf similarity index 100% rename from example/byok/main.tf rename to example/cluster/byok/main.tf diff --git a/example/gcp/main.tf b/example/cluster/gcp/main.tf similarity index 100% rename from example/gcp/main.tf rename to example/cluster/gcp/main.tf diff --git a/example/cluster_data/main.tf b/example/cluster_data/main.tf deleted file mode 100644 index 01c6f19..0000000 --- a/example/cluster_data/main.tf +++ /dev/null @@ -1,16 +0,0 @@ -terraform { - required_providers { - plural = { - source = "pluralsh/plural" - version = "0.0.1" - } - } -} - -provider "plural" { - use_cli = true -} - -data "plural_cluster" "cluster" { - handle = "mgmt" -} \ No newline at end of file diff --git a/example/stack/main.tf b/example/stack/main.tf new file mode 100644 index 0000000..24f16b2 --- /dev/null +++ b/example/stack/main.tf @@ -0,0 +1,57 @@ +terraform { + required_providers { + plural = { + source = "pluralsh/plural" + version = "0.0.1" + } + } +} + +provider "plural" { + use_cli = true +} + +data "plural_cluster" "cluster" { + handle = "mgmt" +} + +data "plural_git_repository" "repository" { + url = "https://github.com/zreigz/tf-hello.git" +} + +resource "plural_infrastructure_stack" "stack" { + name = "tf-stack" + type = "TERRAFORM" + # approval = false + cluster_id = data.plural_cluster.cluster.id + repository = { + id = data.plural_git_repository.repository.id + ref = "main" + folder = "terraform" + } + configuration = { + # image = "" + version = "1.5.7" + } + # files = {} + environment = [ + { + name = "USERNAME" + value = "joe" + }, + { + name = "PASSWORD" + value = "test" + secret = true + } + ] + job_spec = { + namespace = "default" + # raw = "" + # ... + } + bindings = { + read = [] + write = [] + } +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 3dd3ae4..0bb22fb 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -184,6 +184,7 @@ func (p *PluralProvider) Resources(_ context.Context) []func() resource.Resource r.NewProviderResource, r.NewServiceDeploymentResource, r.NewServiceContextResource, + r.NewInfrastructureStackResource, } } diff --git a/internal/resource/infrastructure_stack_schema.go b/internal/resource/infrastructure_stack_schema.go index 2241452..244b275 100644 --- a/internal/resource/infrastructure_stack_schema.go +++ b/internal/resource/infrastructure_stack_schema.go @@ -1,6 +1,8 @@ package resource import ( + "fmt" + "github.com/hashicorp/terraform-plugin-framework-validators/mapvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" @@ -30,8 +32,8 @@ func (r *InfrastructureStackResource) schema() schema.Schema { PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, }, "type": schema.StringAttribute{ - Description: "A type for the stack, specifies the tool to use to apply it.", - MarkdownDescription: "A type for the stack, specifies the tool to use to apply it.", + Description: fmt.Sprintf("A type for the stack, specifies the tool to use to apply it. Allowed values include \"%s\" and \"%s\".", gqlclient.StackTypeAnsible, gqlclient.StackTypeTerraform), + MarkdownDescription: fmt.Sprintf("A type for the stack, specifies the tool to use to apply it. Allowed values include `%s` and `%s`.", gqlclient.StackTypeAnsible, gqlclient.StackTypeTerraform), Required: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, Validators: []validator.String{stringvalidator.OneOf(string(gqlclient.StackTypeAnsible), string(gqlclient.StackTypeTerraform))}, From 6f005072b766efdf0e3a6e86d03b88e0003b500c Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 22 Apr 2024 12:14:41 +0200 Subject: [PATCH 12/57] adjust types --- internal/resource/infrastructure_stack_model.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index 8a99a2b..dd6ce75 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -75,12 +75,17 @@ type InfrastructureStackRepository struct { Folder types.String `tfsdk:"folder"` } -func (isr *InfrastructureStackRepository) From(repository *gqlclient.GitRepositoryFragment, ref gqlclient.GitRefFragment) { +func (isr *InfrastructureStackRepository) From(repository *gqlclient.GitRepositoryFragment, ref *gqlclient.GitRefFragment) { if isr == nil { return } isr.Id = types.StringValue(repository.ID) + + if ref == nil { + return + } + isr.Ref = types.StringValue(ref.Ref) isr.Folder = types.StringValue(ref.Folder) } @@ -90,8 +95,8 @@ type InfrastructureStackConfiguration struct { Version types.String `tfsdk:"version"` } -func (isc *InfrastructureStackConfiguration) From(configuration gqlclient.StackConfigurationFragment) { - if isc == nil { +func (isc *InfrastructureStackConfiguration) From(configuration *gqlclient.StackConfigurationFragment) { + if isc == nil || configuration == nil { return } @@ -111,7 +116,7 @@ var InfrastructureStackEnvironmentAttrTypes = map[string]attr.Type{ "secret": types.BoolType, } -func infrastructureStackEnvironmentsFrom(envs []*gqlclient.StackEnvironment, ctx context.Context, d diag.Diagnostics) types.Set { +func infrastructureStackEnvironmentsFrom(envs []*gqlclient.StackEnvironmentFragment, ctx context.Context, d diag.Diagnostics) types.Set { if len(envs) == 0 { return types.SetNull(basetypes.ObjectType{AttrTypes: InfrastructureStackEnvironmentAttrTypes}) } From 1b006ac457b43cc82d07c70feb04c9485e057a2c Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 22 Apr 2024 12:17:05 +0200 Subject: [PATCH 13/57] fix types --- internal/common/cluster_bindings.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/common/cluster_bindings.go b/internal/common/cluster_bindings.go index 18d3a73..b9ded06 100644 --- a/internal/common/cluster_bindings.go +++ b/internal/common/cluster_bindings.go @@ -51,7 +51,7 @@ func clusterPolicyBindingAttributes(bindings types.Set, ctx context.Context, d d return result } -func (cb *ClusterBindings) From(readBindings []*console.PolicyBinding, writeBindings []*console.PolicyBinding, ctx context.Context, d diag.Diagnostics) { +func (cb *ClusterBindings) From(readBindings []*console.PolicyBindingFragment, writeBindings []*console.PolicyBindingFragment, ctx context.Context, d diag.Diagnostics) { if cb == nil { return } @@ -60,7 +60,7 @@ func (cb *ClusterBindings) From(readBindings []*console.PolicyBinding, writeBind cb.Write = clusterBindingsFrom(writeBindings, ctx, d) } -func clusterBindingsFrom(bindings []*console.PolicyBinding, ctx context.Context, d diag.Diagnostics) types.Set { +func clusterBindingsFrom(bindings []*console.PolicyBindingFragment, ctx context.Context, d diag.Diagnostics) types.Set { if len(bindings) == 0 { return types.SetNull(basetypes.ObjectType{AttrTypes: ClusterPolicyBindingAttrTypes}) } From 9cec9404eec4583b3b59a1e50c8d2870c4147564 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 22 Apr 2024 12:18:23 +0200 Subject: [PATCH 14/57] generate docs --- docs/resources/cluster.md | 4 +- docs/resources/infrastructure_stack.md | 136 ++++++++++++++++++ .../resource/infrastructure_stack_schema.go | 3 - 3 files changed, 138 insertions(+), 5 deletions(-) create mode 100644 docs/resources/infrastructure_stack.md diff --git a/docs/resources/cluster.md b/docs/resources/cluster.md index f8fe6b6..2cdbaa1 100644 --- a/docs/resources/cluster.md +++ b/docs/resources/cluster.md @@ -138,7 +138,7 @@ Required: Optional: - `args` (List of String) Arguments to pass to the command when executing it. -- `env` (Map of String) Defines environment variables to expose to the process. +- `env` (Map of String) Defines environment variables to expose to the process. @@ -186,7 +186,7 @@ Required: Optional: - `args` (List of String) Arguments to pass to the command when executing it. -- `env` (Map of String) Defines environment variables to expose to the process. +- `env` (Map of String) Defines environment variables to expose to the process. diff --git a/docs/resources/infrastructure_stack.md b/docs/resources/infrastructure_stack.md new file mode 100644 index 0000000..d01dd80 --- /dev/null +++ b/docs/resources/infrastructure_stack.md @@ -0,0 +1,136 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "plural_infrastructure_stack Resource - terraform-provider-plural" +subcategory: "" +description: |- + +--- + +# plural_infrastructure_stack (Resource) + + + + + + +## Schema + +### Required + +- `cluster_id` (String) The cluster on which the stack will be applied. +- `configuration` (Attributes) Stack configuration. (see [below for nested schema](#nestedatt--configuration)) +- `name` (String) Human-readable name of this stack. +- `repository` (Attributes) Repository information used to pull stack. (see [below for nested schema](#nestedatt--repository)) +- `type` (String) A type for the stack, specifies the tool to use to apply it. Allowed values include `ANSIBLE` and `TERRAFORM`. + +### Optional + +- `approval` (Boolean) Determines whether to require approval. +- `bindings` (Attributes) Read and write policies of this stack. (see [below for nested schema](#nestedatt--bindings)) +- `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)) + +### Read-Only + +- `id` (String) Internal identifier of this stack. + + +### Nested Schema for `configuration` + +Required: + +- `version` (String) The semver of the tool you wish to use. + +Optional: + +- `image` (String) Optional custom image you might want to use. + + + +### Nested Schema for `repository` + +Required: + +- `folder` (String) The folder where manifests live. +- `id` (String) ID of the repository to pull from. +- `ref` (String) A general git ref, either a branch name or commit sha understandable by "git checkout ". + + + +### Nested Schema for `bindings` + +Optional: + +- `read` (Attributes Set) Read policies of this stack. (see [below for nested schema](#nestedatt--bindings--read)) +- `write` (Attributes Set) Write policies of this stack. (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) + + + + +### Nested Schema for `environment` + +Required: + +- `name` (String) Environment variable name. +- `value` (String) Environment variable value. + +Optional: + +- `secret` (Boolean) Indicates if environment variable is secret. + + + +### Nested Schema for `job_spec` + +Required: + +- `namespace` (String) Namespace where job will be deployed. + +Optional: + +- `annotations` (Map of String) Kubernetes annotations applied to the job. +- `containers` (Attributes Set) (see [below for nested schema](#nestedatt--job_spec--containers)) +- `labels` (Map of String) Kubernetes labels applied to the job. +- `raw` (String) If you'd rather define the job spec via straight Kubernetes YAML. +- `service_account` (String) Kubernetes service account for this job. + + +### Nested Schema for `job_spec.containers` + +Required: + +- `image` (String) + +Optional: + +- `args` (Set of String) Arguments to pass to the command when executing it. +- `env` (Map of String) Defines environment variables to expose to the process. +- `env_from` (Attributes Set) (see [below for nested schema](#nestedatt--job_spec--containers--env_from)) + + +### Nested Schema for `job_spec.containers.env_from` + +Required: + +- `config_map` (String) +- `secret` (String) diff --git a/internal/resource/infrastructure_stack_schema.go b/internal/resource/infrastructure_stack_schema.go index 244b275..a28c2b2 100644 --- a/internal/resource/infrastructure_stack_schema.go +++ b/internal/resource/infrastructure_stack_schema.go @@ -8,7 +8,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" @@ -42,7 +41,6 @@ func (r *InfrastructureStackResource) schema() schema.Schema { Description: "Determines whether to require approval.", MarkdownDescription: "Determines whether to require approval.", Optional: true, - Default: booldefault.StaticBool(false), }, "cluster_id": schema.StringAttribute{ Description: "The cluster on which the stack will be applied.", @@ -114,7 +112,6 @@ func (r *InfrastructureStackResource) schema() schema.Schema { Description: "Indicates if environment variable is secret.", MarkdownDescription: "Indicates if environment variable is secret.", Optional: true, - Default: booldefault.StaticBool(false), }, }, }, From d5b324287a09c1acb5d41d283382b6568ed63a70 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 22 Apr 2024 12:35:18 +0200 Subject: [PATCH 15/57] update attributes --- .../resource/infrastructure_stack_model.go | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index dd6ce75..874aae7 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -32,9 +32,9 @@ func (is *infrastructureStack) Attributes(ctx context.Context, d diag.Diagnostic Type: gqlclient.StackType(is.Type.ValueString()), RepositoryID: is.Repository.Id.ValueString(), ClusterID: is.ClusterId.ValueString(), - Git: gqlclient.GitRefAttributes{}, + Git: is.Repository.Attributes(), JobSpec: nil, - Configuration: gqlclient.StackConfigurationAttributes{}, + Configuration: is.Configuration.Attributes(), Approval: is.Approval.ValueBoolPointer(), ReadBindings: is.Bindings.ReadAttributes(ctx, d), WriteBindings: is.Bindings.WriteAttributes(ctx, d), @@ -75,6 +75,17 @@ type InfrastructureStackRepository struct { Folder types.String `tfsdk:"folder"` } +func (isr *InfrastructureStackRepository) Attributes() gqlclient.GitRefAttributes { + if isr == nil { + return gqlclient.GitRefAttributes{} + } + + return gqlclient.GitRefAttributes{ + Ref: isr.Ref.ValueString(), + Folder: isr.Folder.ValueString(), + } +} + func (isr *InfrastructureStackRepository) From(repository *gqlclient.GitRepositoryFragment, ref *gqlclient.GitRefFragment) { if isr == nil { return @@ -95,6 +106,17 @@ type InfrastructureStackConfiguration struct { Version types.String `tfsdk:"version"` } +func (isc *InfrastructureStackConfiguration) Attributes() gqlclient.StackConfigurationAttributes { + if isc == nil { + return gqlclient.StackConfigurationAttributes{} + } + + return gqlclient.StackConfigurationAttributes{ + Image: isc.Image.ValueStringPointer(), + Version: isc.Version.ValueString(), + } +} + func (isc *InfrastructureStackConfiguration) From(configuration *gqlclient.StackConfigurationFragment) { if isc == nil || configuration == nil { return From babc90d8100f1de149b4a01f17d3edaa046f78f7 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 22 Apr 2024 12:39:08 +0200 Subject: [PATCH 16/57] update attributes --- internal/resource/infrastructure_stack_model.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index 874aae7..9b97c8b 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -33,7 +33,7 @@ func (is *infrastructureStack) Attributes(ctx context.Context, d diag.Diagnostic RepositoryID: is.Repository.Id.ValueString(), ClusterID: is.ClusterId.ValueString(), Git: is.Repository.Attributes(), - JobSpec: nil, + JobSpec: is.JobSpec.Attributes(), Configuration: is.Configuration.Attributes(), Approval: is.Approval.ValueBoolPointer(), ReadBindings: is.Bindings.ReadAttributes(ctx, d), @@ -179,6 +179,14 @@ type InfrastructureStackJobSpec struct { ServiceAccount types.String `tfsdk:"service_account"` } +func (isjs *InfrastructureStackJobSpec) Attributes() *gqlclient.GateJobAttributes { + if isjs == nil { + return nil + } + + return &gqlclient.GateJobAttributes{} // TODO +} + func (isjs *InfrastructureStackJobSpec) From(spec *gqlclient.JobGateSpecFragment) { if isjs == nil { return From e7b4725e9a0afcf3b0cb7237306cbbe3055edee6 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 22 Apr 2024 12:42:28 +0200 Subject: [PATCH 17/57] do not run checks twice in pull requests --- .github/workflows/test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0ba73c5..b230914 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,6 +4,8 @@ on: paths-ignore: - 'README.md' push: + branches: + - "main" paths-ignore: - 'README.md' permissions: From b40fd9d318ee8110ccffdc131452efdc36502d47 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 22 Apr 2024 12:59:25 +0200 Subject: [PATCH 18/57] update attributes --- .../resource/infrastructure_stack_model.go | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index 9b97c8b..6dfd925 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -27,6 +27,7 @@ type infrastructureStack struct { } func (is *infrastructureStack) Attributes(ctx context.Context, d diag.Diagnostics) gqlclient.StackAttributes { + return gqlclient.StackAttributes{ Name: is.Name.ValueString(), Type: gqlclient.StackType(is.Type.ValueString()), @@ -39,8 +40,28 @@ func (is *infrastructureStack) Attributes(ctx context.Context, d diag.Diagnostic ReadBindings: is.Bindings.ReadAttributes(ctx, d), WriteBindings: is.Bindings.WriteAttributes(ctx, d), Files: nil, - Environemnt: nil, + Environemnt: environmentAttributes(is.Environment, ctx, d), + } +} + +func environmentAttributes(environment types.Set, ctx context.Context, d diag.Diagnostics) []*gqlclient.StackEnvironmentAttributes { + if environment.IsNull() { + return nil } + + result := make([]*gqlclient.StackEnvironmentAttributes, 0, len(environment.Elements())) + elements := make([]InfrastructureStackEnvironment, len(environment.Elements())) + d.Append(environment.ElementsAs(ctx, &elements, false)...) + + for _, env := range elements { + result = append(result, &gqlclient.StackEnvironmentAttributes{ + Name: env.Name.ValueString(), + Value: env.Value.ValueString(), + Secret: env.Secret.ValueBoolPointer(), + }) + } + + return result } func (is *infrastructureStack) From(stack *gqlclient.InfrastructureStackFragment, ctx context.Context, d diag.Diagnostics) { From 760595a3f6efbd311eb0b678a6c77d5a55765e3c Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 22 Apr 2024 13:14:35 +0200 Subject: [PATCH 19/57] update attributes --- .../resource/infrastructure_stack_model.go | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index 6dfd925..a8cb903 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -27,7 +27,6 @@ type infrastructureStack struct { } func (is *infrastructureStack) Attributes(ctx context.Context, d diag.Diagnostics) gqlclient.StackAttributes { - return gqlclient.StackAttributes{ Name: is.Name.ValueString(), Type: gqlclient.StackType(is.Type.ValueString()), @@ -39,19 +38,31 @@ func (is *infrastructureStack) Attributes(ctx context.Context, d diag.Diagnostic Approval: is.Approval.ValueBoolPointer(), ReadBindings: is.Bindings.ReadAttributes(ctx, d), WriteBindings: is.Bindings.WriteAttributes(ctx, d), - Files: nil, - Environemnt: environmentAttributes(is.Environment, ctx, d), + Files: is.FilesAttributes(ctx, d), + Environemnt: is.EnvironmentAttributes(ctx, d), } } -func environmentAttributes(environment types.Set, ctx context.Context, d diag.Diagnostics) []*gqlclient.StackEnvironmentAttributes { - if environment.IsNull() { +func (is *infrastructureStack) FilesAttributes(ctx context.Context, d diag.Diagnostics) []*gqlclient.StackFileAttributes { + result := make([]*gqlclient.StackFileAttributes, 0) + elements := make(map[string]types.String, len(is.Files.Elements())) + d.Append(is.Files.ElementsAs(ctx, &elements, false)...) + + for k, v := range elements { + result = append(result, &gqlclient.StackFileAttributes{Path: k, Content: v.ValueString()}) + } + + return result +} + +func (is *infrastructureStack) EnvironmentAttributes(ctx context.Context, d diag.Diagnostics) []*gqlclient.StackEnvironmentAttributes { + if is.Environment.IsNull() { return nil } - result := make([]*gqlclient.StackEnvironmentAttributes, 0, len(environment.Elements())) - elements := make([]InfrastructureStackEnvironment, len(environment.Elements())) - d.Append(environment.ElementsAs(ctx, &elements, false)...) + result := make([]*gqlclient.StackEnvironmentAttributes, 0, len(is.Environment.Elements())) + elements := make([]InfrastructureStackEnvironment, len(is.Environment.Elements())) + d.Append(is.Environment.ElementsAs(ctx, &elements, false)...) for _, env := range elements { result = append(result, &gqlclient.StackEnvironmentAttributes{ From 5140648fc68299afe91bddbde5946f6b4b749c53 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 22 Apr 2024 13:43:46 +0200 Subject: [PATCH 20/57] update attributes --- .../resource/infrastructure_stack_model.go | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index a8cb903..5f1f080 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -33,7 +33,7 @@ func (is *infrastructureStack) Attributes(ctx context.Context, d diag.Diagnostic RepositoryID: is.Repository.Id.ValueString(), ClusterID: is.ClusterId.ValueString(), Git: is.Repository.Attributes(), - JobSpec: is.JobSpec.Attributes(), + JobSpec: is.JobSpec.Attributes(ctx, d), Configuration: is.Configuration.Attributes(), Approval: is.Approval.ValueBoolPointer(), ReadBindings: is.Bindings.ReadAttributes(ctx, d), @@ -211,12 +211,39 @@ type InfrastructureStackJobSpec struct { ServiceAccount types.String `tfsdk:"service_account"` } -func (isjs *InfrastructureStackJobSpec) Attributes() *gqlclient.GateJobAttributes { +func (isjs *InfrastructureStackJobSpec) Attributes(ctx context.Context, d diag.Diagnostics) *gqlclient.GateJobAttributes { if isjs == nil { return nil } - return &gqlclient.GateJobAttributes{} // TODO + return &gqlclient.GateJobAttributes{ + Namespace: isjs.Namespace.ValueString(), + Raw: isjs.Raw.ValueStringPointer(), + Containers: nil, // TODO + Labels: isjs.LabelsAttributes(ctx, d), + Annotations: isjs.AnnotationsAttributes(ctx, d), + ServiceAccount: isjs.ServiceAccount.ValueStringPointer(), + } +} + +func (isjs *InfrastructureStackJobSpec) LabelsAttributes(ctx context.Context, d diag.Diagnostics) *string { + if isjs.Labels.IsNull() { + return nil + } + + elements := make(map[string]types.String, len(isjs.Labels.Elements())) + d.Append(isjs.Labels.ElementsAs(ctx, &elements, false)...) + return common.AttributesJson(elements, d) +} + +func (isjs *InfrastructureStackJobSpec) AnnotationsAttributes(ctx context.Context, d diag.Diagnostics) *string { + if isjs.Annotations.IsNull() { + return nil + } + + elements := make(map[string]types.String, len(isjs.Annotations.Elements())) + d.Append(isjs.Annotations.ElementsAs(ctx, &elements, false)...) + return common.AttributesJson(elements, d) } func (isjs *InfrastructureStackJobSpec) From(spec *gqlclient.JobGateSpecFragment) { From 1f6f8d27213356c84f0e5423589fe61ae43b6ba6 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 22 Apr 2024 14:10:09 +0200 Subject: [PATCH 21/57] update from --- internal/common/cluster_node_pool.go | 12 +---- internal/common/map.go | 18 ++++++++ .../resource/infrastructure_stack_model.go | 44 ++++++++++++++++--- 3 files changed, 58 insertions(+), 16 deletions(-) create mode 100644 internal/common/map.go diff --git a/internal/common/cluster_node_pool.go b/internal/common/cluster_node_pool.go index 0de8c68..08c59ce 100644 --- a/internal/common/cluster_node_pool.go +++ b/internal/common/cluster_node_pool.go @@ -141,7 +141,7 @@ func ClusterNodePoolsFrom(nodePools []*console.NodePoolFragment, configNodePools MinSize: types.Int64Value(nodePool.MinSize), MaxSize: types.Int64Value(nodePool.MaxSize), InstanceType: types.StringValue(nodePool.InstanceType), - Labels: clusterNodePoolLabelsFrom(nodePool, ctx, d), + Labels: MapFrom(nodePool.Labels, ctx, d), Taints: clusterNodePoolTaintsFrom(nodePool, ctx, d), CloudSettings: configNodePoolsElements[nodePool.Name].CloudSettings, // Rewriting config to state to avoid unknown values. }).Element() @@ -154,16 +154,6 @@ func ClusterNodePoolsFrom(nodePools []*console.NodePoolFragment, configNodePools return mapValue } -func clusterNodePoolLabelsFrom(nodePool *console.NodePoolFragment, ctx context.Context, d diag.Diagnostics) types.Map { - if len(nodePool.Labels) == 0 { - return types.MapNull(types.StringType) - } - - mapValue, diags := types.MapValueFrom(ctx, types.StringType, nodePool.Labels) - d.Append(diags...) - return mapValue -} - func clusterNodePoolTaintsFrom(nodePool *console.NodePoolFragment, ctx context.Context, d diag.Diagnostics) types.Set { if len(nodePool.Taints) == 0 { return types.SetNull(basetypes.ObjectType{AttrTypes: NodePoolTaintAttrTypes}) diff --git a/internal/common/map.go b/internal/common/map.go new file mode 100644 index 0000000..dd678fd --- /dev/null +++ b/internal/common/map.go @@ -0,0 +1,18 @@ +package common + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func MapFrom(values map[string]any, ctx context.Context, d diag.Diagnostics) types.Map { + if len(values) == 0 { + return types.MapNull(types.StringType) + } + + mapValue, diags := types.MapValueFrom(ctx, types.StringType, values) + d.Append(diags...) + return mapValue +} diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index 5f1f080..d892982 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -86,7 +86,7 @@ func (is *infrastructureStack) From(stack *gqlclient.InfrastructureStackFragment is.Files = infrastructureStackFilesFrom(stack.Files, d) is.Environment = infrastructureStackEnvironmentsFrom(stack.Environment, ctx, d) is.Bindings.From(stack.ReadBindings, stack.WriteBindings, ctx, d) - is.JobSpec.From(stack.JobSpec) + is.JobSpec.From(stack.JobSpec, ctx, d) } func infrastructureStackFilesFrom(files []*gqlclient.StackFileFragment, d diag.Diagnostics) basetypes.MapValue { @@ -246,16 +246,16 @@ func (isjs *InfrastructureStackJobSpec) AnnotationsAttributes(ctx context.Contex return common.AttributesJson(elements, d) } -func (isjs *InfrastructureStackJobSpec) From(spec *gqlclient.JobGateSpecFragment) { +func (isjs *InfrastructureStackJobSpec) From(spec *gqlclient.JobGateSpecFragment, ctx context.Context, d diag.Diagnostics) { if isjs == nil { return } isjs.Namespace = types.StringValue(spec.Namespace) isjs.Raw = types.StringPointerValue(spec.Raw) - // TODO: Containers - // TODO: Labels - // TODO: Annotations + isjs.Containers = infrastructureStackJobSpecContainersFrom(spec.Containers, ctx, d) + isjs.Labels = common.MapFrom(spec.Labels, ctx, d) + isjs.Annotations = common.MapFrom(spec.Annotations, ctx, d) isjs.ServiceAccount = types.StringPointerValue(spec.ServiceAccount) } @@ -266,7 +266,41 @@ type InfrastructureStackContainerSpec struct { EnvFrom types.Set `tfsdk:"env_from"` } +var InfrastructureStackContainerSpecAttrTypes = map[string]attr.Type{ + "image": types.StringType, + "args": types.SetType{ElemType: types.StringType}, + "env": types.MapType{ElemType: types.StringType}, + "env_from": types.SetType{ElemType: types.ObjectType{AttrTypes: InfrastructureStackContainerEnvFromAttrTypes}}, +} + +func infrastructureStackJobSpecContainersFrom(containers []*gqlclient.ContainerSpecFragment, ctx context.Context, d diag.Diagnostics) types.Set { + if len(containers) == 0 { + return types.SetNull(basetypes.ObjectType{AttrTypes: InfrastructureStackContainerSpecAttrTypes}) + } + + values := make([]attr.Value, len(containers)) + for i, container := range containers { + objValue, diags := types.ObjectValueFrom(ctx, InfrastructureStackContainerSpecAttrTypes, InfrastructureStackContainerSpec{ + Image: types.StringValue(container.Image), + Args: types.Set{}, + Env: types.Map{}, + EnvFrom: types.Set{}, + }) + values[i] = objValue + d.Append(diags...) + } + + setValue, diags := types.SetValue(basetypes.ObjectType{AttrTypes: InfrastructureStackContainerSpecAttrTypes}, values) + d.Append(diags...) + return setValue +} + type InfrastructureStackContainerEnvFrom struct { Secret types.String `tfsdk:"secret"` ConfigMap types.String `tfsdk:"config_map"` } + +var InfrastructureStackContainerEnvFromAttrTypes = map[string]attr.Type{ + "secret": types.StringType, + "config_map": types.StringType, +} From 5c9c4e09e53011e8507878bc1ed62e26a22c69db Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 22 Apr 2024 14:31:36 +0200 Subject: [PATCH 22/57] update from --- .../resource/infrastructure_stack_model.go | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index d892982..1839762 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -259,20 +259,6 @@ func (isjs *InfrastructureStackJobSpec) From(spec *gqlclient.JobGateSpecFragment isjs.ServiceAccount = types.StringPointerValue(spec.ServiceAccount) } -type InfrastructureStackContainerSpec struct { - Image types.String `tfsdk:"image"` - Args types.Set `tfsdk:"args"` - Env types.Map `tfsdk:"env"` - EnvFrom types.Set `tfsdk:"env_from"` -} - -var InfrastructureStackContainerSpecAttrTypes = map[string]attr.Type{ - "image": types.StringType, - "args": types.SetType{ElemType: types.StringType}, - "env": types.MapType{ElemType: types.StringType}, - "env_from": types.SetType{ElemType: types.ObjectType{AttrTypes: InfrastructureStackContainerEnvFromAttrTypes}}, -} - func infrastructureStackJobSpecContainersFrom(containers []*gqlclient.ContainerSpecFragment, ctx context.Context, d diag.Diagnostics) types.Set { if len(containers) == 0 { return types.SetNull(basetypes.ObjectType{AttrTypes: InfrastructureStackContainerSpecAttrTypes}) @@ -282,7 +268,7 @@ func infrastructureStackJobSpecContainersFrom(containers []*gqlclient.ContainerS for i, container := range containers { objValue, diags := types.ObjectValueFrom(ctx, InfrastructureStackContainerSpecAttrTypes, InfrastructureStackContainerSpec{ Image: types.StringValue(container.Image), - Args: types.Set{}, + Args: infrastructureStackContainerSpecArgsFrom(container.Args, ctx, d), Env: types.Map{}, EnvFrom: types.Set{}, }) @@ -295,6 +281,30 @@ func infrastructureStackJobSpecContainersFrom(containers []*gqlclient.ContainerS return setValue } +func infrastructureStackContainerSpecArgsFrom(values []*string, ctx context.Context, d diag.Diagnostics) types.Set { + if len(values) == 0 { + return types.SetNull(types.StringType) + } + + setValue, diags := types.SetValueFrom(ctx, types.StringType, values) + d.Append(diags...) + return setValue +} + +type InfrastructureStackContainerSpec struct { + Image types.String `tfsdk:"image"` + Args types.Set `tfsdk:"args"` + Env types.Map `tfsdk:"env"` + EnvFrom types.Set `tfsdk:"env_from"` +} + +var InfrastructureStackContainerSpecAttrTypes = map[string]attr.Type{ + "image": types.StringType, + "args": types.SetType{ElemType: types.StringType}, + "env": types.MapType{ElemType: types.StringType}, + "env_from": types.SetType{ElemType: types.ObjectType{AttrTypes: InfrastructureStackContainerEnvFromAttrTypes}}, +} + type InfrastructureStackContainerEnvFrom struct { Secret types.String `tfsdk:"secret"` ConfigMap types.String `tfsdk:"config_map"` From 4de3c520f46d1e9f93e11d7a6c772a8009cf6610 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 22 Apr 2024 15:03:12 +0200 Subject: [PATCH 23/57] update attributes --- .../resource/infrastructure_stack_model.go | 35 +++++++++++++++++-- internal/resource/service_context_model.go | 12 +------ 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index 1839762..d59a540 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -219,7 +219,7 @@ func (isjs *InfrastructureStackJobSpec) Attributes(ctx context.Context, d diag.D return &gqlclient.GateJobAttributes{ Namespace: isjs.Namespace.ValueString(), Raw: isjs.Raw.ValueStringPointer(), - Containers: nil, // TODO + Containers: isjs.ContainersAttributes(ctx, d), Labels: isjs.LabelsAttributes(ctx, d), Annotations: isjs.AnnotationsAttributes(ctx, d), ServiceAccount: isjs.ServiceAccount.ValueStringPointer(), @@ -246,6 +246,22 @@ func (isjs *InfrastructureStackJobSpec) AnnotationsAttributes(ctx context.Contex return common.AttributesJson(elements, d) } +func (isjs *InfrastructureStackJobSpec) ContainersAttributes(ctx context.Context, d diag.Diagnostics) []*gqlclient.ContainerAttributes { + if isjs.Containers.IsNull() { + return nil + } + + result := make([]*gqlclient.ContainerAttributes, 0, len(isjs.Containers.Elements())) + elements := make([]InfrastructureStackContainerSpec, len(isjs.Containers.Elements())) + d.Append(isjs.Containers.ElementsAs(ctx, &elements, false)...) + + for _, container := range elements { + result = append(result, container.Attributes(ctx, d)) + } + + return result +} + func (isjs *InfrastructureStackJobSpec) From(spec *gqlclient.JobGateSpecFragment, ctx context.Context, d diag.Diagnostics) { if isjs == nil { return @@ -269,8 +285,8 @@ func infrastructureStackJobSpecContainersFrom(containers []*gqlclient.ContainerS objValue, diags := types.ObjectValueFrom(ctx, InfrastructureStackContainerSpecAttrTypes, InfrastructureStackContainerSpec{ Image: types.StringValue(container.Image), Args: infrastructureStackContainerSpecArgsFrom(container.Args, ctx, d), - Env: types.Map{}, - EnvFrom: types.Set{}, + Env: types.Map{}, // TODO + EnvFrom: types.Set{}, // TODO }) values[i] = objValue d.Append(diags...) @@ -305,6 +321,19 @@ var InfrastructureStackContainerSpecAttrTypes = map[string]attr.Type{ "env_from": types.SetType{ElemType: types.ObjectType{AttrTypes: InfrastructureStackContainerEnvFromAttrTypes}}, } +func (iscs *InfrastructureStackContainerSpec) Attributes(ctx context.Context, d diag.Diagnostics) *gqlclient.ContainerAttributes { + if iscs == nil { + return nil + } + + return &gqlclient.ContainerAttributes{ + Image: iscs.Image.ValueString(), + Args: nil, // TODO + Env: nil, // TODO + EnvFrom: nil, // TODO + } +} + type InfrastructureStackContainerEnvFrom struct { Secret types.String `tfsdk:"secret"` ConfigMap types.String `tfsdk:"config_map"` diff --git a/internal/resource/service_context_model.go b/internal/resource/service_context_model.go index 6c36f2f..7a55c92 100644 --- a/internal/resource/service_context_model.go +++ b/internal/resource/service_context_model.go @@ -19,17 +19,7 @@ type serviceContext struct { func (sc *serviceContext) From(scf *console.ServiceContextFragment, ctx context.Context, d diag.Diagnostics) { sc.Id = types.StringValue(scf.ID) - sc.Configuration = serviceContextConfigurationFrom(scf.Configuration, ctx, d) -} - -func serviceContextConfigurationFrom(configuration map[string]any, ctx context.Context, d diag.Diagnostics) types.Map { - if len(configuration) == 0 { - return types.MapNull(types.StringType) - } - - mapValue, diags := types.MapValueFrom(ctx, types.StringType, configuration) - d.Append(diags...) - return mapValue + sc.Configuration = common.MapFrom(scf.Configuration, ctx, d) } func (sc *serviceContext) Attributes(ctx context.Context, d diag.Diagnostics) console.ServiceContextAttributes { From ba5aa5c3f5453bd3af889a7af93496e205bf2d6b Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 22 Apr 2024 15:12:39 +0200 Subject: [PATCH 24/57] update attributes --- internal/resource/infrastructure_stack_model.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index d59a540..0bce828 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" gqlclient "github.com/pluralsh/console-client-go" + "github.com/pluralsh/polly/algorithms" ) type infrastructureStack struct { @@ -328,12 +329,22 @@ func (iscs *InfrastructureStackContainerSpec) Attributes(ctx context.Context, d return &gqlclient.ContainerAttributes{ Image: iscs.Image.ValueString(), - Args: nil, // TODO + Args: iscs.ArgsAttributes(ctx, d), Env: nil, // TODO EnvFrom: nil, // TODO } } +func (isjs *InfrastructureStackContainerSpec) ArgsAttributes(ctx context.Context, d diag.Diagnostics) []*string { + if isjs.Args.IsNull() { + return nil + } + + elements := make([]types.String, len(isjs.Args.Elements())) + d.Append(isjs.Args.ElementsAs(ctx, &elements, false)...) + return algorithms.Map(elements, func(v types.String) *string { return v.ValueStringPointer() }) +} + type InfrastructureStackContainerEnvFrom struct { Secret types.String `tfsdk:"secret"` ConfigMap types.String `tfsdk:"config_map"` From 6ff4a3471b789717eaa2bf52af5d3867c8b78402 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 22 Apr 2024 15:41:26 +0200 Subject: [PATCH 25/57] update attributes --- .../resource/infrastructure_stack_model.go | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index 0bce828..5b3f427 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -331,7 +331,7 @@ func (iscs *InfrastructureStackContainerSpec) Attributes(ctx context.Context, d Image: iscs.Image.ValueString(), Args: iscs.ArgsAttributes(ctx, d), Env: nil, // TODO - EnvFrom: nil, // TODO + EnvFrom: iscs.EnvFromAttributes(ctx, d), } } @@ -345,6 +345,25 @@ func (isjs *InfrastructureStackContainerSpec) ArgsAttributes(ctx context.Context return algorithms.Map(elements, func(v types.String) *string { return v.ValueStringPointer() }) } +func (isjs *InfrastructureStackContainerSpec) EnvFromAttributes(ctx context.Context, d diag.Diagnostics) []*gqlclient.EnvFromAttributes { + if isjs.EnvFrom.IsNull() { + return nil + } + + result := make([]*gqlclient.EnvFromAttributes, 0, len(isjs.EnvFrom.Elements())) + elements := make([]InfrastructureStackContainerEnvFrom, len(isjs.EnvFrom.Elements())) + d.Append(isjs.EnvFrom.ElementsAs(ctx, &elements, false)...) + + for _, envFrom := range elements { + result = append(result, &gqlclient.EnvFromAttributes{ + Secret: envFrom.Secret.ValueString(), + ConfigMap: envFrom.ConfigMap.ValueString(), + }) + } + + return result +} + type InfrastructureStackContainerEnvFrom struct { Secret types.String `tfsdk:"secret"` ConfigMap types.String `tfsdk:"config_map"` From b0856571c76b8c10418fc0e37909b0dde1ea1295 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 22 Apr 2024 15:47:46 +0200 Subject: [PATCH 26/57] finish attributes --- .../resource/infrastructure_stack_model.go | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index 5b3f427..4850c89 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -330,7 +330,7 @@ func (iscs *InfrastructureStackContainerSpec) Attributes(ctx context.Context, d return &gqlclient.ContainerAttributes{ Image: iscs.Image.ValueString(), Args: iscs.ArgsAttributes(ctx, d), - Env: nil, // TODO + Env: iscs.EnvAttributes(ctx, d), EnvFrom: iscs.EnvFromAttributes(ctx, d), } } @@ -345,6 +345,22 @@ func (isjs *InfrastructureStackContainerSpec) ArgsAttributes(ctx context.Context return algorithms.Map(elements, func(v types.String) *string { return v.ValueStringPointer() }) } +func (isjs *InfrastructureStackContainerSpec) EnvAttributes(ctx context.Context, d diag.Diagnostics) []*gqlclient.EnvAttributes { + if isjs.Env.IsNull() { + return nil + } + + result := make([]*gqlclient.EnvAttributes, 0) + elements := make(map[string]types.String, len(isjs.Env.Elements())) + d.Append(isjs.Env.ElementsAs(ctx, &elements, false)...) + + for k, v := range elements { + result = append(result, &gqlclient.EnvAttributes{Name: k, Value: v.ValueString()}) + } + + return result +} + func (isjs *InfrastructureStackContainerSpec) EnvFromAttributes(ctx context.Context, d diag.Diagnostics) []*gqlclient.EnvFromAttributes { if isjs.EnvFrom.IsNull() { return nil From 6ae55619dcd0886ae120d0c05e11cc4df0c8c158 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 22 Apr 2024 15:57:57 +0200 Subject: [PATCH 27/57] update from --- internal/resource/infrastructure_stack_model.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index 4850c89..9e0e8c2 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -286,7 +286,7 @@ func infrastructureStackJobSpecContainersFrom(containers []*gqlclient.ContainerS objValue, diags := types.ObjectValueFrom(ctx, InfrastructureStackContainerSpecAttrTypes, InfrastructureStackContainerSpec{ Image: types.StringValue(container.Image), Args: infrastructureStackContainerSpecArgsFrom(container.Args, ctx, d), - Env: types.Map{}, // TODO + Env: infrastructureStackContainerSpecEnvFrom(container.Env, ctx, d), EnvFrom: types.Set{}, // TODO }) values[i] = objValue @@ -308,6 +308,18 @@ func infrastructureStackContainerSpecArgsFrom(values []*string, ctx context.Cont return setValue } +func infrastructureStackContainerSpecEnvFrom(env []*gqlclient.ContainerSpecFragment_Env, ctx context.Context, d diag.Diagnostics) basetypes.MapValue { + resultMap := map[string]attr.Value{} + for _, v := range env { + resultMap[v.Name] = types.StringValue(v.Value) + } + + result, tagsDiagnostics := types.MapValue(types.StringType, resultMap) + d.Append(tagsDiagnostics...) + + return result +} + type InfrastructureStackContainerSpec struct { Image types.String `tfsdk:"image"` Args types.Set `tfsdk:"args"` From 1b8489c85ec62c3914ffad8707303940dada338e Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 22 Apr 2024 16:13:26 +0200 Subject: [PATCH 28/57] finish mappings --- .../resource/infrastructure_stack_model.go | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index 9e0e8c2..2729c70 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -286,8 +286,8 @@ func infrastructureStackJobSpecContainersFrom(containers []*gqlclient.ContainerS objValue, diags := types.ObjectValueFrom(ctx, InfrastructureStackContainerSpecAttrTypes, InfrastructureStackContainerSpec{ Image: types.StringValue(container.Image), Args: infrastructureStackContainerSpecArgsFrom(container.Args, ctx, d), - Env: infrastructureStackContainerSpecEnvFrom(container.Env, ctx, d), - EnvFrom: types.Set{}, // TODO + Env: infrastructureStackContainerSpecEnvFrom(container.Env, d), + EnvFrom: infrastructureStackContainerSpecEnvFromFrom(container.EnvFrom, ctx, d), }) values[i] = objValue d.Append(diags...) @@ -308,7 +308,7 @@ func infrastructureStackContainerSpecArgsFrom(values []*string, ctx context.Cont return setValue } -func infrastructureStackContainerSpecEnvFrom(env []*gqlclient.ContainerSpecFragment_Env, ctx context.Context, d diag.Diagnostics) basetypes.MapValue { +func infrastructureStackContainerSpecEnvFrom(env []*gqlclient.ContainerSpecFragment_Env, d diag.Diagnostics) types.Map { resultMap := map[string]attr.Value{} for _, v := range env { resultMap[v.Name] = types.StringValue(v.Value) @@ -320,6 +320,26 @@ func infrastructureStackContainerSpecEnvFrom(env []*gqlclient.ContainerSpecFragm return result } +func infrastructureStackContainerSpecEnvFromFrom(envFroms []*gqlclient.ContainerSpecFragment_EnvFrom, ctx context.Context, d diag.Diagnostics) types.Set { + if len(envFroms) == 0 { + return types.SetNull(basetypes.ObjectType{AttrTypes: InfrastructureStackContainerEnvFromAttrTypes}) + } + + values := make([]attr.Value, len(envFroms)) + for i, envFrom := range envFroms { + objValue, diags := types.ObjectValueFrom(ctx, InfrastructureStackContainerEnvFromAttrTypes, InfrastructureStackContainerEnvFrom{ + ConfigMap: types.StringValue(envFrom.ConfigMap), + Secret: types.StringValue(envFrom.Secret), + }) + values[i] = objValue + d.Append(diags...) + } + + setValue, diags := types.SetValue(basetypes.ObjectType{AttrTypes: InfrastructureStackContainerEnvFromAttrTypes}, values) + d.Append(diags...) + return setValue +} + type InfrastructureStackContainerSpec struct { Image types.String `tfsdk:"image"` Args types.Set `tfsdk:"args"` From 68d32bed871dbb7dfb7bd9ef6c92929b00b19b6c Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 22 Apr 2024 16:14:08 +0200 Subject: [PATCH 29/57] update docs --- internal/validator/also_requires_if.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/validator/also_requires_if.go b/internal/validator/also_requires_if.go index 4180e28..1affbea 100644 --- a/internal/validator/also_requires_if.go +++ b/internal/validator/also_requires_if.go @@ -117,7 +117,6 @@ func (a alsoRequiresIfValidator) ValidateString(ctx context.Context, req validat resp.Diagnostics.Append(validateResp.Diagnostics...) } -// AlsoRequiresIf todo. func AlsoRequiresIf(f RequiresIf, expressions ...path.Expression) validator.String { return &alsoRequiresIfValidator{ PathExpressions: expressions, From 9234b42571b85ee22104391d928c9b07370e5d18 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 22 Apr 2024 16:31:11 +0200 Subject: [PATCH 30/57] update validators --- .../resource/infrastructure_stack_schema.go | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/internal/resource/infrastructure_stack_schema.go b/internal/resource/infrastructure_stack_schema.go index a28c2b2..86b2d3e 100644 --- a/internal/resource/infrastructure_stack_schema.go +++ b/internal/resource/infrastructure_stack_schema.go @@ -131,12 +131,8 @@ func (r *InfrastructureStackResource) schema() schema.Schema { MarkdownDescription: "If you'd rather define the job spec via straight Kubernetes YAML.", Optional: true, Validators: []validator.String{ - stringvalidator.ExactlyOneOf( - path.MatchRelative().AtParent().AtName("labels"), - path.MatchRelative().AtParent().AtName("annotations"), - path.MatchRelative().AtParent().AtName("service_account"), - path.MatchRelative().AtParent().AtName("containers"), - ), + stringvalidator.ExactlyOneOf(path.MatchRelative().AtParent().AtName("containers")), + stringvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("containers")), }, }, "labels": schema.MapAttribute{ @@ -144,20 +140,20 @@ func (r *InfrastructureStackResource) schema() schema.Schema { MarkdownDescription: "Kubernetes labels applied to the job.", ElementType: types.StringType, Optional: true, - Validators: []validator.Map{mapvalidator.ExactlyOneOf(path.MatchRelative().AtParent().AtName("raw"))}, + Validators: []validator.Map{mapvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("raw"))}, }, "annotations": schema.MapAttribute{ Description: "Kubernetes annotations applied to the job.", MarkdownDescription: "Kubernetes annotations applied to the job.", ElementType: types.StringType, Optional: true, - Validators: []validator.Map{mapvalidator.ExactlyOneOf(path.MatchRelative().AtParent().AtName("raw"))}, + Validators: []validator.Map{mapvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("raw"))}, }, "service_account": schema.StringAttribute{ Description: "Kubernetes service account for this job.", MarkdownDescription: "Kubernetes service account for this job.", Optional: true, - Validators: []validator.String{stringvalidator.ExactlyOneOf(path.MatchRelative().AtParent().AtName("raw"))}, + Validators: []validator.String{stringvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("raw"))}, }, "containers": schema.SetNestedAttribute{ Optional: true, @@ -193,7 +189,10 @@ func (r *InfrastructureStackResource) schema() schema.Schema { }, }, }, - Validators: []validator.Set{setvalidator.ExactlyOneOf(path.MatchRelative().AtParent().AtName("raw"))}, + Validators: []validator.Set{ + setvalidator.ExactlyOneOf(path.MatchRelative().AtParent().AtName("raw")), + setvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("raw")), + }, }, }, }, From 90e837105c9d8923307aadfc1962565ee26074a1 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 22 Apr 2024 16:38:33 +0200 Subject: [PATCH 31/57] fix files model --- example/stack/main.tf | 4 ++-- internal/resource/infrastructure_stack_model.go | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/example/stack/main.tf b/example/stack/main.tf index 24f16b2..dfaa4a8 100644 --- a/example/stack/main.tf +++ b/example/stack/main.tf @@ -20,7 +20,7 @@ data "plural_git_repository" "repository" { } resource "plural_infrastructure_stack" "stack" { - name = "tf-stack" + name = "tf-stack-2" type = "TERRAFORM" # approval = false cluster_id = data.plural_cluster.cluster.id @@ -47,7 +47,7 @@ resource "plural_infrastructure_stack" "stack" { ] job_spec = { namespace = "default" - # raw = "" + raw = "" # ... } bindings = { diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index 2729c70..c216a19 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -17,7 +17,7 @@ type infrastructureStack struct { Id types.String `tfsdk:"id"` Name types.String `tfsdk:"name"` Type types.String `tfsdk:"type"` - Approval types.Bool `tfsdk:"protect"` + Approval types.Bool `tfsdk:"approval"` ClusterId types.String `tfsdk:"cluster_id"` Repository *InfrastructureStackRepository `tfsdk:"repository"` Configuration *InfrastructureStackConfiguration `tfsdk:"configuration"` @@ -45,6 +45,10 @@ func (is *infrastructureStack) Attributes(ctx context.Context, d diag.Diagnostic } func (is *infrastructureStack) FilesAttributes(ctx context.Context, d diag.Diagnostics) []*gqlclient.StackFileAttributes { + if is.Files.IsNull() { + return nil + } + result := make([]*gqlclient.StackFileAttributes, 0) elements := make(map[string]types.String, len(is.Files.Elements())) d.Append(is.Files.ElementsAs(ctx, &elements, false)...) From 449bddfecd9b3790a753005b3d7ca1932bd7a08b Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 22 Apr 2024 16:46:05 +0200 Subject: [PATCH 32/57] fix files model --- example/stack/main.tf | 4 ++-- internal/resource/infrastructure_stack_model.go | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/example/stack/main.tf b/example/stack/main.tf index dfaa4a8..2455857 100644 --- a/example/stack/main.tf +++ b/example/stack/main.tf @@ -20,9 +20,9 @@ data "plural_git_repository" "repository" { } resource "plural_infrastructure_stack" "stack" { - name = "tf-stack-2" + name = "tf-stack-4" type = "TERRAFORM" - # approval = false + approval = true cluster_id = data.plural_cluster.cluster.id repository = { id = data.plural_git_repository.repository.id diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index c216a19..6d7f1df 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -95,6 +95,10 @@ func (is *infrastructureStack) From(stack *gqlclient.InfrastructureStackFragment } func infrastructureStackFilesFrom(files []*gqlclient.StackFileFragment, d diag.Diagnostics) basetypes.MapValue { + if len(files) == 0 { + return types.MapNull(types.StringType) + } + resultMap := make(map[string]attr.Value, len(files)) for _, file := range files { resultMap[file.Path] = types.StringValue(file.Content) From 806aad3d63ec0b8fb40ef9be4cd651bd0940e82d Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 22 Apr 2024 18:01:39 +0200 Subject: [PATCH 33/57] update example --- example/stack/main.tf | 23 +++++++++++++------ internal/common/cluster_bindings.go | 4 ++-- .../resource/infrastructure_stack_schema.go | 3 +++ 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/example/stack/main.tf b/example/stack/main.tf index 2455857..56ff735 100644 --- a/example/stack/main.tf +++ b/example/stack/main.tf @@ -20,7 +20,7 @@ data "plural_git_repository" "repository" { } resource "plural_infrastructure_stack" "stack" { - name = "tf-stack-4" + name = "tf-stack-13" type = "TERRAFORM" approval = true cluster_id = data.plural_cluster.cluster.id @@ -30,10 +30,12 @@ resource "plural_infrastructure_stack" "stack" { folder = "terraform" } configuration = { - # image = "" - version = "1.5.7" + image = "hashicorp/terraform:1.8.1" + version = "1.8.1" + } + files = { + # "test.yml": "value: 123" } - # files = {} environment = [ { name = "USERNAME" @@ -47,11 +49,18 @@ resource "plural_infrastructure_stack" "stack" { ] job_spec = { namespace = "default" - raw = "" + raw = jsonencode({ + containers = jsonencode([{ + name = "pi" + image = "perl:5.34.0" + command = jsonencode(["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]) + }]) + restartPolicy = "Never" + }) # ... } bindings = { - read = [] - write = [] + read = [] + write = [] } } diff --git a/internal/common/cluster_bindings.go b/internal/common/cluster_bindings.go index b9ded06..e29fb30 100644 --- a/internal/common/cluster_bindings.go +++ b/internal/common/cluster_bindings.go @@ -17,7 +17,7 @@ type ClusterBindings struct { func (cb *ClusterBindings) ReadAttributes(ctx context.Context, d diag.Diagnostics) []*console.PolicyBindingAttributes { if cb == nil { - return []*console.PolicyBindingAttributes{} + return nil } return clusterPolicyBindingAttributes(cb.Read, ctx, d) @@ -25,7 +25,7 @@ func (cb *ClusterBindings) ReadAttributes(ctx context.Context, d diag.Diagnostic func (cb *ClusterBindings) WriteAttributes(ctx context.Context, d diag.Diagnostics) []*console.PolicyBindingAttributes { if cb == nil { - return []*console.PolicyBindingAttributes{} + return nil } return clusterPolicyBindingAttributes(cb.Write, ctx, d) diff --git a/internal/resource/infrastructure_stack_schema.go b/internal/resource/infrastructure_stack_schema.go index 86b2d3e..da62520 100644 --- a/internal/resource/infrastructure_stack_schema.go +++ b/internal/resource/infrastructure_stack_schema.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/path" "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" "github.com/hashicorp/terraform-plugin-framework/schema/validator" @@ -130,6 +131,7 @@ func (r *InfrastructureStackResource) schema() schema.Schema { Description: "If you'd rather define the job spec via straight Kubernetes YAML.", MarkdownDescription: "If you'd rather define the job spec via straight Kubernetes YAML.", Optional: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, Validators: []validator.String{ stringvalidator.ExactlyOneOf(path.MatchRelative().AtParent().AtName("containers")), stringvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("containers")), @@ -238,6 +240,7 @@ func (r *InfrastructureStackResource) schema() schema.Schema { }, }, }, + PlanModifiers: []planmodifier.Object{objectplanmodifier.UseStateForUnknown()}, }, }, } From 6feba726a91e8aa129b461a2c63e525e59b89999 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 23 Apr 2024 09:22:02 +0200 Subject: [PATCH 34/57] randomize example stack names --- example/stack/main.tf | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/example/stack/main.tf b/example/stack/main.tf index 56ff735..2467c44 100644 --- a/example/stack/main.tf +++ b/example/stack/main.tf @@ -19,8 +19,14 @@ data "plural_git_repository" "repository" { url = "https://github.com/zreigz/tf-hello.git" } +resource "random_string" "random" { + length = 5 + upper = false + special = false +} + resource "plural_infrastructure_stack" "stack" { - name = "tf-stack-13" + name = "stack-tf-${random_string.random.result}" type = "TERRAFORM" approval = true cluster_id = data.plural_cluster.cluster.id From e679f1140b887d9bc24779bdcc1ae754d67abee4 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 23 Apr 2024 09:51:04 +0200 Subject: [PATCH 35/57] fix bindings --- internal/common/cluster_bindings.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/common/cluster_bindings.go b/internal/common/cluster_bindings.go index e29fb30..ceac7f1 100644 --- a/internal/common/cluster_bindings.go +++ b/internal/common/cluster_bindings.go @@ -61,7 +61,7 @@ func (cb *ClusterBindings) From(readBindings []*console.PolicyBindingFragment, w } func clusterBindingsFrom(bindings []*console.PolicyBindingFragment, ctx context.Context, d diag.Diagnostics) types.Set { - if len(bindings) == 0 { + if bindings == nil { return types.SetNull(basetypes.ObjectType{AttrTypes: ClusterPolicyBindingAttrTypes}) } From 34120ef6ee8dd5ddfbb9087e094eea00ad9a9ae3 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 23 Apr 2024 10:02:11 +0200 Subject: [PATCH 36/57] fix environment --- internal/resource/infrastructure_stack_model.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index 6d7f1df..8ce4c3b 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -95,7 +95,7 @@ func (is *infrastructureStack) From(stack *gqlclient.InfrastructureStackFragment } func infrastructureStackFilesFrom(files []*gqlclient.StackFileFragment, d diag.Diagnostics) basetypes.MapValue { - if len(files) == 0 { + if files == nil { return types.MapNull(types.StringType) } @@ -180,7 +180,7 @@ var InfrastructureStackEnvironmentAttrTypes = map[string]attr.Type{ } func infrastructureStackEnvironmentsFrom(envs []*gqlclient.StackEnvironmentFragment, ctx context.Context, d diag.Diagnostics) types.Set { - if len(envs) == 0 { + if envs == nil { return types.SetNull(basetypes.ObjectType{AttrTypes: InfrastructureStackEnvironmentAttrTypes}) } From a62a6ea9c25033a8616b22475b7685ea1d2dcc03 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 23 Apr 2024 10:15:57 +0200 Subject: [PATCH 37/57] refactor --- internal/defaults/env.go | 4 ++-- internal/resource/cluster_kubeconfig.go | 6 +++--- internal/resource/cluster_operator_handler.go | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/internal/defaults/env.go b/internal/defaults/env.go index 11b1a54..b1c104b 100644 --- a/internal/defaults/env.go +++ b/internal/defaults/env.go @@ -38,7 +38,7 @@ func (d envDefaultValue[_]) MarkdownDescription(_ context.Context) string { } func (d envDefaultValue[T]) DefaultString(_ context.Context, _ defaults.StringRequest, resp *defaults.StringResponse) { - value := interface{}(d.defaultValue) + value := any(d.defaultValue) if v := os.Getenv(d.envVar); len(v) > 0 { value = v } @@ -48,7 +48,7 @@ func (d envDefaultValue[T]) DefaultString(_ context.Context, _ defaults.StringRe } func (d envDefaultValue[T]) DefaultBool(_ context.Context, _ defaults.BoolRequest, resp *defaults.BoolResponse) { - value := interface{}(d.defaultValue) + value := any(d.defaultValue) if v := os.Getenv(d.envVar); len(v) > 0 { value = v == "true" } diff --git a/internal/resource/cluster_kubeconfig.go b/internal/resource/cluster_kubeconfig.go index f1cc462..7978ce1 100644 --- a/internal/resource/cluster_kubeconfig.go +++ b/internal/resource/cluster_kubeconfig.go @@ -66,7 +66,7 @@ func newKubeconfig(ctx context.Context, kubeconfig *Kubeconfig, namespace *strin loader := &clientcmd.ClientConfigLoadingRules{} if !lo.IsEmpty(kubeconfig.ConfigPath.ValueString()) { - tflog.Info(ctx, "using kubeconfig", map[string]interface{}{ + tflog.Info(ctx, "using kubeconfig", map[string]any{ "kubeconfig": kubeconfig.ConfigPath.ValueString(), }) @@ -79,7 +79,7 @@ func newKubeconfig(ctx context.Context, kubeconfig *Kubeconfig, namespace *strin if !lo.IsEmpty(kubeconfig.ConfigContext.ValueString()) || !lo.IsEmpty(kubeconfig.ConfigContextAuthInfo.ValueString()) || !lo.IsEmpty(kubeconfig.ConfigContextCluster.ValueString()) { if !lo.IsEmpty(kubeconfig.ConfigContext.ValueString()) { overrides.CurrentContext = kubeconfig.ConfigContext.ValueString() - tflog.Info(ctx, "using custom current context", map[string]interface{}{ + tflog.Info(ctx, "using custom current context", map[string]any{ "context": overrides.CurrentContext, }) } @@ -91,7 +91,7 @@ func newKubeconfig(ctx context.Context, kubeconfig *Kubeconfig, namespace *strin if !lo.IsEmpty(kubeconfig.ConfigContextCluster.ValueString()) { overrides.Context.Cluster = kubeconfig.ConfigContextCluster.ValueString() } - tflog.Info(ctx, "using overridden context", map[string]interface{}{ + tflog.Info(ctx, "using overridden context", map[string]any{ "context": overrides.Context, }) } diff --git a/internal/resource/cluster_operator_handler.go b/internal/resource/cluster_operator_handler.go index 4cc0efe..326a7a2 100644 --- a/internal/resource/cluster_operator_handler.go +++ b/internal/resource/cluster_operator_handler.go @@ -40,7 +40,7 @@ type OperatorHandler struct { repoUrl string // additional values used on install - vals map[string]interface{} + vals map[string]any // Preconfigured helm actions and chart chart *chart.Chart @@ -152,9 +152,9 @@ func (oh *OperatorHandler) listReleases(state action.ListStates) ([]*release.Rel return client.Run() } -func (oh *OperatorHandler) values(token string) (map[string]interface{}, error) { - globalVals := map[string]interface{}{} - vals := map[string]interface{}{ +func (oh *OperatorHandler) values(token string) (map[string]any, error) { + globalVals := map[string]any{} + vals := map[string]any{ "secrets": map[string]string{ "deployToken": token, }, @@ -232,7 +232,7 @@ func (oh *OperatorHandler) Uninstall() error { } func NewOperatorHandler(ctx context.Context, client *client.Client, kubeconfig *Kubeconfig, repoUrl string, values *string, consoleUrl string) (*OperatorHandler, error) { - vals := map[string]interface{}{} + vals := map[string]any{} if values != nil { if err := yaml.Unmarshal([]byte(*values), &vals); err != nil { return nil, err From 625fda3ed5e0944588a63094264e791afe19f3fe Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 23 Apr 2024 12:28:25 +0200 Subject: [PATCH 38/57] bug fixes --- docs/resources/infrastructure_stack.md | 2 +- example/stack/main.tf | 49 ++++++++++++------- .../resource/infrastructure_stack_model.go | 22 +++++---- .../resource/infrastructure_stack_schema.go | 2 +- 4 files changed, 46 insertions(+), 29 deletions(-) diff --git a/docs/resources/infrastructure_stack.md b/docs/resources/infrastructure_stack.md index d01dd80..ca072fa 100644 --- a/docs/resources/infrastructure_stack.md +++ b/docs/resources/infrastructure_stack.md @@ -123,7 +123,7 @@ Required: Optional: -- `args` (Set of String) Arguments to pass to the command when executing it. +- `args` (List of String) Arguments to pass to the command when executing it. - `env` (Map of String) Defines environment variables to expose to the process. - `env_from` (Attributes Set) (see [below for nested schema](#nestedatt--job_spec--containers--env_from)) diff --git a/example/stack/main.tf b/example/stack/main.tf index 2467c44..249f286 100644 --- a/example/stack/main.tf +++ b/example/stack/main.tf @@ -25,6 +25,7 @@ resource "random_string" "random" { special = false } +# TODO: Test deletion. resource "plural_infrastructure_stack" "stack" { name = "stack-tf-${random_string.random.result}" type = "TERRAFORM" @@ -40,30 +41,42 @@ resource "plural_infrastructure_stack" "stack" { version = "1.8.1" } files = { + # TODO: Test it. # "test.yml": "value: 123" } environment = [ - { - name = "USERNAME" - value = "joe" - }, - { - name = "PASSWORD" - value = "test" - secret = true - } + # TODO: Test it. + # { + # name = "USERNAME" + # value = "joe" + # }, + # { + # name = "PASSWORD" + # value = "test" + # secret = true + # } ] job_spec = { namespace = "default" - raw = jsonencode({ - containers = jsonencode([{ - name = "pi" - image = "perl:5.34.0" - command = jsonencode(["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]) - }]) - restartPolicy = "Never" - }) - # ... + labels = { + test = "123" + } + service_account = "default" + containers = [{ + image = "perl:5.34.0" + args = ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"] + # TODO: Test without env and env_from. + env = {} + env_from = [] + }] + # raw = jsonencode({ + # containers = jsonencode([{ + # name = "pi" + # image = "perl:5.34.0" + # command = jsonencode(["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]) + # }]) + # restartPolicy = "Never" + # }) } bindings = { read = [] diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index 8ce4c3b..eb1823f 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -285,7 +285,7 @@ func (isjs *InfrastructureStackJobSpec) From(spec *gqlclient.JobGateSpecFragment } func infrastructureStackJobSpecContainersFrom(containers []*gqlclient.ContainerSpecFragment, ctx context.Context, d diag.Diagnostics) types.Set { - if len(containers) == 0 { + if containers == nil { return types.SetNull(basetypes.ObjectType{AttrTypes: InfrastructureStackContainerSpecAttrTypes}) } @@ -306,17 +306,21 @@ func infrastructureStackJobSpecContainersFrom(containers []*gqlclient.ContainerS return setValue } -func infrastructureStackContainerSpecArgsFrom(values []*string, ctx context.Context, d diag.Diagnostics) types.Set { - if len(values) == 0 { - return types.SetNull(types.StringType) +func infrastructureStackContainerSpecArgsFrom(values []*string, ctx context.Context, d diag.Diagnostics) types.List { + if values == nil { + return types.ListNull(types.StringType) } - setValue, diags := types.SetValueFrom(ctx, types.StringType, values) + listValue, diags := types.ListValueFrom(ctx, types.StringType, values) d.Append(diags...) - return setValue + return listValue } func infrastructureStackContainerSpecEnvFrom(env []*gqlclient.ContainerSpecFragment_Env, d diag.Diagnostics) types.Map { + if env == nil { + return types.MapNull(types.StringType) + } + resultMap := map[string]attr.Value{} for _, v := range env { resultMap[v.Name] = types.StringValue(v.Value) @@ -329,7 +333,7 @@ func infrastructureStackContainerSpecEnvFrom(env []*gqlclient.ContainerSpecFragm } func infrastructureStackContainerSpecEnvFromFrom(envFroms []*gqlclient.ContainerSpecFragment_EnvFrom, ctx context.Context, d diag.Diagnostics) types.Set { - if len(envFroms) == 0 { + if envFroms == nil { return types.SetNull(basetypes.ObjectType{AttrTypes: InfrastructureStackContainerEnvFromAttrTypes}) } @@ -350,14 +354,14 @@ func infrastructureStackContainerSpecEnvFromFrom(envFroms []*gqlclient.Container type InfrastructureStackContainerSpec struct { Image types.String `tfsdk:"image"` - Args types.Set `tfsdk:"args"` + Args types.List `tfsdk:"args"` Env types.Map `tfsdk:"env"` EnvFrom types.Set `tfsdk:"env_from"` } var InfrastructureStackContainerSpecAttrTypes = map[string]attr.Type{ "image": types.StringType, - "args": types.SetType{ElemType: types.StringType}, + "args": types.ListType{ElemType: types.StringType}, "env": types.MapType{ElemType: types.StringType}, "env_from": types.SetType{ElemType: types.ObjectType{AttrTypes: InfrastructureStackContainerEnvFromAttrTypes}}, } diff --git a/internal/resource/infrastructure_stack_schema.go b/internal/resource/infrastructure_stack_schema.go index da62520..1c8f8d0 100644 --- a/internal/resource/infrastructure_stack_schema.go +++ b/internal/resource/infrastructure_stack_schema.go @@ -164,7 +164,7 @@ func (r *InfrastructureStackResource) schema() schema.Schema { "image": schema.StringAttribute{ Required: true, }, - "args": schema.SetAttribute{ + "args": schema.ListAttribute{ Description: "Arguments to pass to the command when executing it.", MarkdownDescription: "Arguments to pass to the command when executing it.", Optional: true, From 33c832ddfb4fba633951405b4c8e2d10f4571f36 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 23 Apr 2024 12:38:26 +0200 Subject: [PATCH 39/57] update examples --- example/cluster/byok/main.tf | 4 ++-- example/stack/main.tf | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/example/cluster/byok/main.tf b/example/cluster/byok/main.tf index 5cd75a7..cffceb5 100644 --- a/example/cluster/byok/main.tf +++ b/example/cluster/byok/main.tf @@ -21,9 +21,9 @@ resource "plural_cluster" "byok" { metadata = jsonencode({ test1 = "test" test2 = false - test3 = jsonencode({ + test3 = { abc = false - }) + } }) helm_repo_url = "https://pluralsh.github.io/deployment-operator" tags = { diff --git a/example/stack/main.tf b/example/stack/main.tf index 249f286..3442663 100644 --- a/example/stack/main.tf +++ b/example/stack/main.tf @@ -69,14 +69,14 @@ resource "plural_infrastructure_stack" "stack" { env = {} env_from = [] }] - # raw = jsonencode({ - # containers = jsonencode([{ - # name = "pi" - # image = "perl:5.34.0" - # command = jsonencode(["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]) - # }]) - # restartPolicy = "Never" - # }) +# raw = jsonencode({ +# containers = [{ +# name = "pi" +# image = "perl:5.34.0" +# command = ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"] +# }] +# restartPolicy = "Never" +# }) } bindings = { read = [] From f6d88354821b45bcb2f3c09fa3be0fef880e2664 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 23 Apr 2024 12:48:31 +0200 Subject: [PATCH 40/57] update examples --- example/stack/main.tf | 152 +++++++++++++++++++++++++++++++----------- 1 file changed, 114 insertions(+), 38 deletions(-) diff --git a/example/stack/main.tf b/example/stack/main.tf index 3442663..cd76f4f 100644 --- a/example/stack/main.tf +++ b/example/stack/main.tf @@ -20,41 +20,39 @@ data "plural_git_repository" "repository" { } resource "random_string" "random" { - length = 5 - upper = false + length = 5 + upper = false special = false } # TODO: Test deletion. -resource "plural_infrastructure_stack" "stack" { - name = "stack-tf-${random_string.random.result}" - type = "TERRAFORM" - approval = true +resource "plural_infrastructure_stack" "stack-full" { + name = "stack-tf-full-${random_string.random.result}" + type = "TERRAFORM" + approval = true cluster_id = data.plural_cluster.cluster.id repository = { - id = data.plural_git_repository.repository.id - ref = "main" + id = data.plural_git_repository.repository.id + ref = "main" folder = "terraform" } configuration = { - image = "hashicorp/terraform:1.8.1" + image = "hashicorp/terraform:1.8.1" version = "1.8.1" } files = { - # TODO: Test it. - # "test.yml": "value: 123" + "test.yml" : "value: 123" } environment = [ - # TODO: Test it. - # { - # name = "USERNAME" - # value = "joe" - # }, - # { - # name = "PASSWORD" - # value = "test" - # secret = true - # } + { + name = "USERNAME" + value = "joe" + }, + { + name = "PASSWORD" + value = "test" + secret = true + } ] job_spec = { namespace = "default" @@ -62,24 +60,102 @@ resource "plural_infrastructure_stack" "stack" { test = "123" } service_account = "default" - containers = [{ - image = "perl:5.34.0" - args = ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"] - # TODO: Test without env and env_from. - env = {} - env_from = [] - }] -# raw = jsonencode({ -# containers = [{ -# name = "pi" -# image = "perl:5.34.0" -# command = ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"] -# }] -# restartPolicy = "Never" -# }) + containers = [ + { + image = "perl:5.34.0" + args = ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"] + env = {} + env_from = [] + } + ] } bindings = { - read = [] - write = [] + read = [] + write = [] + } +} + +resource "plural_infrastructure_stack" "stack-raw" { + name = "stack-tf-raw-${random_string.random.result}" + type = "TERRAFORM" + approval = true + cluster_id = data.plural_cluster.cluster.id + repository = { + id = data.plural_git_repository.repository.id + ref = "main" + folder = "terraform" + } + configuration = { + image = "hashicorp/terraform:1.8.1" + version = "1.8.1" + } + files = { + "test.yml" : "value: 123" + } + environment = [ + { + name = "USERNAME" + value = "joe" + }, + { + name = "PASSWORD" + value = "test" + secret = true + } + ] + job_spec = { + namespace = "default" + raw = jsonencode({ + containers = [ + { + name = "pi" + image = "perl:5.34.0" + command = ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"] + } + ] + restartPolicy = "Never" + }) + } + bindings = { + read = [] + write = [] + } +} + +resource "plural_infrastructure_stack" "stack-empty" { + name = "stack-tf-empty-${random_string.random.result}" + type = "TERRAFORM" + approval = true + cluster_id = data.plural_cluster.cluster.id + repository = { + id = data.plural_git_repository.repository.id + ref = "main" + folder = "terraform" + } + configuration = { + version = "1.8.1" + } + files = {} + environment = [] + job_spec = { + namespace = "default" + labels = {} + annotations = {} + containers = [] + } + bindings = {} +} + +resource "plural_infrastructure_stack" "stack-minimal" { + name = "stack-tf-minimal-${random_string.random.result}" + type = "TERRAFORM" + cluster_id = data.plural_cluster.cluster.id + repository = { + id = data.plural_git_repository.repository.id + ref = "main" + folder = "terraform" + } + configuration = { + version = "1.8.1" } } From 369c2bd7381876daff2446e120264df2ac7dc776 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 23 Apr 2024 14:53:01 +0200 Subject: [PATCH 41/57] update client --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index cf5c16a..3d30a28 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.2.0 + github.com/pluralsh/console-client-go v0.4.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 95016e2..850b0bc 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.2.0 h1:60BPwsyZ9uwAUcFUxGyXRX8C+Pk/W5Dvvvk3R64m0UI= -github.com/pluralsh/console-client-go v0.2.0/go.mod h1:eyCiLA44YbXiYyJh8303jk5JdPkt9McgCo5kBjk4lKo= +github.com/pluralsh/console-client-go v0.4.0 h1:lgKaVGi8jB7S8wFF6L3P6H/4Xc88e4FozhyW58O1w3Q= +github.com/pluralsh/console-client-go v0.4.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 a2701ad5418346e497ac855ed8a8f44f1ec95382 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 23 Apr 2024 14:54:37 +0200 Subject: [PATCH 42/57] allow stack detach --- internal/resource/infrastructure_stack.go | 41 +++++++++++-------- .../resource/infrastructure_stack_model.go | 3 +- .../resource/infrastructure_stack_schema.go | 7 ++++ 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/internal/resource/infrastructure_stack.go b/internal/resource/infrastructure_stack.go index 88e41bb..5bee9ae 100644 --- a/internal/resource/infrastructure_stack.go +++ b/internal/resource/infrastructure_stack.go @@ -5,13 +5,12 @@ import ( "fmt" "time" + "terraform-provider-plural/internal/client" "terraform-provider-plural/internal/common" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "k8s.io/apimachinery/pkg/util/wait" - - "terraform-provider-plural/internal/client" ) var _ resource.Resource = &InfrastructureStackResource{} @@ -109,23 +108,31 @@ func (r *InfrastructureStackResource) Delete(ctx context.Context, req resource.D return } - _, err := r.client.DeleteStack(ctx, data.Id.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete infrastructure stack, got error: %s", err)) - return - } - - err = wait.WaitForWithContext(ctx, client.Ticker(5*time.Second), func(ctx context.Context) (bool, error) { - _, err := r.client.GetInfrastructureStack(ctx, data.Id.ValueString()) - if client.IsNotFound(err) { - return true, nil + if data.Detach.ValueBool() { + _, err := r.client.DetachStack(ctx, data.Id.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to detach infrastructure stack, got error: %s", err)) + return + } + } else { + _, err := r.client.DeleteStack(ctx, data.Id.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete infrastructure stack, got error: %s", err)) + return } - return false, err - }) - if err != nil { - resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Error during watiting for infrastructure stack to be deleted, got error: %s", err)) - return + err = wait.WaitForWithContext(ctx, client.Ticker(5*time.Second), func(ctx context.Context) (bool, error) { + _, err := r.client.GetInfrastructureStack(ctx, data.Id.ValueString()) + if client.IsNotFound(err) { + return true, nil + } + + return false, err + }) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Error during watiting for infrastructure stack to be deleted, got error: %s", err)) + return + } } } diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index eb1823f..ba9646a 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -18,6 +18,7 @@ type infrastructureStack struct { Name types.String `tfsdk:"name"` Type types.String `tfsdk:"type"` Approval types.Bool `tfsdk:"approval"` + Detach types.Bool `tfsdk:"detach"` ClusterId types.String `tfsdk:"cluster_id"` Repository *InfrastructureStackRepository `tfsdk:"repository"` Configuration *InfrastructureStackConfiguration `tfsdk:"configuration"` @@ -40,7 +41,7 @@ func (is *infrastructureStack) Attributes(ctx context.Context, d diag.Diagnostic ReadBindings: is.Bindings.ReadAttributes(ctx, d), WriteBindings: is.Bindings.WriteAttributes(ctx, d), Files: is.FilesAttributes(ctx, d), - Environemnt: is.EnvironmentAttributes(ctx, d), + Environment: is.EnvironmentAttributes(ctx, d), } } diff --git a/internal/resource/infrastructure_stack_schema.go b/internal/resource/infrastructure_stack_schema.go index 1c8f8d0..c9c9a8c 100644 --- a/internal/resource/infrastructure_stack_schema.go +++ b/internal/resource/infrastructure_stack_schema.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" "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" @@ -43,6 +44,12 @@ func (r *InfrastructureStackResource) schema() schema.Schema { MarkdownDescription: "Determines whether to require approval.", Optional: true, }, + "detach": schema.BoolAttribute{ + Description: "Determines behavior during resource destruction, if true it will detach resource instead of deleting it.", + MarkdownDescription: "Determines behavior during resource destruction, if true it will detach resource instead of deleting it.", + Optional: true, + Default: booldefault.StaticBool(false), + }, "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 5e1e283439b78125c2404267169a6004e921f1d0 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 23 Apr 2024 14:59:26 +0200 Subject: [PATCH 43/57] mark detach as computed --- internal/resource/infrastructure_stack_schema.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/resource/infrastructure_stack_schema.go b/internal/resource/infrastructure_stack_schema.go index c9c9a8c..e36c8d6 100644 --- a/internal/resource/infrastructure_stack_schema.go +++ b/internal/resource/infrastructure_stack_schema.go @@ -48,6 +48,7 @@ func (r *InfrastructureStackResource) schema() schema.Schema { Description: "Determines behavior during resource destruction, if true it will detach resource instead of deleting it.", MarkdownDescription: "Determines behavior during resource destruction, if true it will detach resource instead of deleting it.", Optional: true, + Computed: true, Default: booldefault.StaticBool(false), }, "cluster_id": schema.StringAttribute{ From 0b826b80e48e9957b28499b19eaf2d130d46b407 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 23 Apr 2024 15:12:56 +0200 Subject: [PATCH 44/57] rename var --- internal/resource/infrastructure_stack_model.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index ba9646a..be33ac8 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -327,8 +327,8 @@ func infrastructureStackContainerSpecEnvFrom(env []*gqlclient.ContainerSpecFragm resultMap[v.Name] = types.StringValue(v.Value) } - result, tagsDiagnostics := types.MapValue(types.StringType, resultMap) - d.Append(tagsDiagnostics...) + result, diags := types.MapValue(types.StringType, resultMap) + d.Append(diags...) return result } From fc938faced1b668744ce4e68d217569d027ba7a4 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 23 Apr 2024 15:33:29 +0200 Subject: [PATCH 45/57] use detach in examples --- example/stack/main.tf | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/example/stack/main.tf b/example/stack/main.tf index cd76f4f..d5b7cf8 100644 --- a/example/stack/main.tf +++ b/example/stack/main.tf @@ -25,11 +25,11 @@ resource "random_string" "random" { special = false } -# TODO: Test deletion. resource "plural_infrastructure_stack" "stack-full" { name = "stack-tf-full-${random_string.random.result}" type = "TERRAFORM" approval = true + detach = true cluster_id = data.plural_cluster.cluster.id repository = { id = data.plural_git_repository.repository.id @@ -79,6 +79,7 @@ resource "plural_infrastructure_stack" "stack-raw" { name = "stack-tf-raw-${random_string.random.result}" type = "TERRAFORM" approval = true + detach = true cluster_id = data.plural_cluster.cluster.id repository = { id = data.plural_git_repository.repository.id @@ -126,6 +127,7 @@ resource "plural_infrastructure_stack" "stack-empty" { name = "stack-tf-empty-${random_string.random.result}" type = "TERRAFORM" approval = true + detach = true cluster_id = data.plural_cluster.cluster.id repository = { id = data.plural_git_repository.repository.id @@ -138,7 +140,7 @@ resource "plural_infrastructure_stack" "stack-empty" { files = {} environment = [] job_spec = { - namespace = "default" + namespace = "default" labels = {} annotations = {} containers = [] @@ -149,6 +151,7 @@ resource "plural_infrastructure_stack" "stack-empty" { resource "plural_infrastructure_stack" "stack-minimal" { name = "stack-tf-minimal-${random_string.random.result}" type = "TERRAFORM" + detach = true cluster_id = data.plural_cluster.cluster.id repository = { id = data.plural_git_repository.repository.id From 020f0a5a297119e87abf559610f397480b510c55 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 23 Apr 2024 16:11:59 +0200 Subject: [PATCH 46/57] mark some attributes as computed --- internal/resource/infrastructure_stack_schema.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/resource/infrastructure_stack_schema.go b/internal/resource/infrastructure_stack_schema.go index e36c8d6..15b7dc3 100644 --- a/internal/resource/infrastructure_stack_schema.go +++ b/internal/resource/infrastructure_stack_schema.go @@ -99,6 +99,7 @@ func (r *InfrastructureStackResource) schema() schema.Schema { "files": schema.MapAttribute{ MarkdownDescription: "File path-content map.", Optional: true, + Computed: true, ElementType: types.StringType, }, "environment": schema.SetNestedAttribute{ @@ -215,6 +216,7 @@ func (r *InfrastructureStackResource) schema() schema.Schema { Description: "Read policies of this stack.", MarkdownDescription: "Read policies of this stack.", Optional: true, + Computed: true, NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "group_id": schema.StringAttribute{ @@ -233,6 +235,7 @@ func (r *InfrastructureStackResource) schema() schema.Schema { Description: "Write policies of this stack.", MarkdownDescription: "Write policies of this stack.", Optional: true, + Computed: true, NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "group_id": schema.StringAttribute{ From 83b2c2da7029f8d08a144f3e8354e0cce1ceea55 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 23 Apr 2024 16:20:47 +0200 Subject: [PATCH 47/57] mark some more attributes as computed --- docs/resources/infrastructure_stack.md | 1 + internal/resource/infrastructure_stack_schema.go | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/resources/infrastructure_stack.md b/docs/resources/infrastructure_stack.md index ca072fa..0b19547 100644 --- a/docs/resources/infrastructure_stack.md +++ b/docs/resources/infrastructure_stack.md @@ -27,6 +27,7 @@ description: |- - `approval` (Boolean) Determines whether to require approval. - `bindings` (Attributes) Read and write policies of this stack. (see [below for nested schema](#nestedatt--bindings)) +- `detach` (Boolean) Determines behavior during resource destruction, if true it will detach resource instead of deleting it. - `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)) diff --git a/internal/resource/infrastructure_stack_schema.go b/internal/resource/infrastructure_stack_schema.go index 15b7dc3..f37b90e 100644 --- a/internal/resource/infrastructure_stack_schema.go +++ b/internal/resource/infrastructure_stack_schema.go @@ -106,6 +106,7 @@ func (r *InfrastructureStackResource) schema() schema.Schema { Description: "Defines environment variables for the stack.", MarkdownDescription: "Defines environment variables for the stack.", Optional: true, + Computed: true, NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "name": schema.StringAttribute{ From a7cb32fac6d9a4eac7cae007c076a1516c7b2d93 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 23 Apr 2024 16:36:29 +0200 Subject: [PATCH 48/57] mark some more attributes as computed --- internal/resource/infrastructure_stack_schema.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/resource/infrastructure_stack_schema.go b/internal/resource/infrastructure_stack_schema.go index f37b90e..e2ee77a 100644 --- a/internal/resource/infrastructure_stack_schema.go +++ b/internal/resource/infrastructure_stack_schema.go @@ -152,6 +152,7 @@ func (r *InfrastructureStackResource) schema() schema.Schema { MarkdownDescription: "Kubernetes labels applied to the job.", ElementType: types.StringType, Optional: true, + Computed: true, Validators: []validator.Map{mapvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("raw"))}, }, "annotations": schema.MapAttribute{ @@ -159,6 +160,7 @@ func (r *InfrastructureStackResource) schema() schema.Schema { MarkdownDescription: "Kubernetes annotations applied to the job.", ElementType: types.StringType, Optional: true, + Computed: true, Validators: []validator.Map{mapvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("raw"))}, }, "service_account": schema.StringAttribute{ @@ -169,6 +171,7 @@ func (r *InfrastructureStackResource) schema() schema.Schema { }, "containers": schema.SetNestedAttribute{ Optional: true, + Computed: true, NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "image": schema.StringAttribute{ From 703df07bdb8146f73af85ca69d87dc877bdf24f1 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 23 Apr 2024 16:40:06 +0200 Subject: [PATCH 49/57] require at least one container if set --- internal/resource/infrastructure_stack_schema.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/resource/infrastructure_stack_schema.go b/internal/resource/infrastructure_stack_schema.go index e2ee77a..760af24 100644 --- a/internal/resource/infrastructure_stack_schema.go +++ b/internal/resource/infrastructure_stack_schema.go @@ -205,6 +205,7 @@ func (r *InfrastructureStackResource) schema() schema.Schema { }, }, Validators: []validator.Set{ + setvalidator.SizeAtLeast(1), setvalidator.ExactlyOneOf(path.MatchRelative().AtParent().AtName("raw")), setvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("raw")), }, From 2e782fce48f6b247f5b780176e050bf626ae2e79 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 23 Apr 2024 16:47:25 +0200 Subject: [PATCH 50/57] update examples --- example/stack/main.tf | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/example/stack/main.tf b/example/stack/main.tf index d5b7cf8..4a81b3d 100644 --- a/example/stack/main.tf +++ b/example/stack/main.tf @@ -106,7 +106,7 @@ resource "plural_infrastructure_stack" "stack-raw" { ] job_spec = { namespace = "default" - raw = jsonencode({ + raw = yamlencode({ containers = [ { name = "pi" @@ -141,9 +141,7 @@ resource "plural_infrastructure_stack" "stack-empty" { environment = [] job_spec = { namespace = "default" - labels = {} - annotations = {} - containers = [] + raw = yamlencode({}) } bindings = {} } From f1359609c8a5f31247f9e713fb73fec11ada5c36 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 23 Apr 2024 16:47:54 +0200 Subject: [PATCH 51/57] prevent from using empty raw job spec --- internal/resource/infrastructure_stack_schema.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/resource/infrastructure_stack_schema.go b/internal/resource/infrastructure_stack_schema.go index 760af24..06af686 100644 --- a/internal/resource/infrastructure_stack_schema.go +++ b/internal/resource/infrastructure_stack_schema.go @@ -143,6 +143,7 @@ func (r *InfrastructureStackResource) schema() schema.Schema { Optional: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, Validators: []validator.String{ + stringvalidator.LengthAtLeast(1), stringvalidator.ExactlyOneOf(path.MatchRelative().AtParent().AtName("containers")), stringvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("containers")), }, From 80d048ab583fe913f26fa02aa497c67fd0b6ef8e Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 23 Apr 2024 16:52:59 +0200 Subject: [PATCH 52/57] update example --- example/stack/main.tf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/example/stack/main.tf b/example/stack/main.tf index 4a81b3d..b8e2174 100644 --- a/example/stack/main.tf +++ b/example/stack/main.tf @@ -41,7 +41,7 @@ resource "plural_infrastructure_stack" "stack-full" { version = "1.8.1" } files = { - "test.yml" : "value: 123" + "test.yml" = "value: 123" } environment = [ { @@ -91,7 +91,7 @@ resource "plural_infrastructure_stack" "stack-raw" { version = "1.8.1" } files = { - "test.yml" : "value: 123" + "test.yml" = "value: 123" } environment = [ { @@ -140,8 +140,8 @@ resource "plural_infrastructure_stack" "stack-empty" { files = {} environment = [] job_spec = { - namespace = "default" - raw = yamlencode({}) + namespace = "default" + raw = yamlencode({ test = true }) } bindings = {} } From 281231448bd2142e50c0d12d61e974daf55d8d59 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 23 Apr 2024 17:41:16 +0200 Subject: [PATCH 53/57] update description --- internal/resource/infrastructure_stack_schema.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/resource/infrastructure_stack_schema.go b/internal/resource/infrastructure_stack_schema.go index 06af686..08ae8e6 100644 --- a/internal/resource/infrastructure_stack_schema.go +++ b/internal/resource/infrastructure_stack_schema.go @@ -97,6 +97,7 @@ func (r *InfrastructureStackResource) schema() schema.Schema { }, }, "files": schema.MapAttribute{ + Description: "File path-content map.", MarkdownDescription: "File path-content map.", Optional: true, Computed: true, From 1317e52bde32930e8fda6c05e910d3f5a83965ef Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Wed, 24 Apr 2024 11:09:17 +0200 Subject: [PATCH 54/57] add plan modifiers for labels and annotations --- internal/resource/infrastructure_stack_schema.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/resource/infrastructure_stack_schema.go b/internal/resource/infrastructure_stack_schema.go index 08ae8e6..1b60c05 100644 --- a/internal/resource/infrastructure_stack_schema.go +++ b/internal/resource/infrastructure_stack_schema.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier" "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" @@ -155,6 +156,7 @@ func (r *InfrastructureStackResource) schema() schema.Schema { ElementType: types.StringType, Optional: true, Computed: true, + PlanModifiers: []planmodifier.Map{mapplanmodifier.UseStateForUnknown()}, Validators: []validator.Map{mapvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("raw"))}, }, "annotations": schema.MapAttribute{ @@ -163,6 +165,7 @@ func (r *InfrastructureStackResource) schema() schema.Schema { ElementType: types.StringType, Optional: true, Computed: true, + PlanModifiers: []planmodifier.Map{mapplanmodifier.UseStateForUnknown()}, Validators: []validator.Map{mapvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("raw"))}, }, "service_account": schema.StringAttribute{ From 183c8f8e24666976a179983408aacebe3a134ea3 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Wed, 24 Apr 2024 11:48:19 +0200 Subject: [PATCH 55/57] default map and annotations --- internal/common/map.go | 2 +- internal/resource/infrastructure_stack_schema.go | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/internal/common/map.go b/internal/common/map.go index dd678fd..8e11724 100644 --- a/internal/common/map.go +++ b/internal/common/map.go @@ -8,7 +8,7 @@ import ( ) func MapFrom(values map[string]any, ctx context.Context, d diag.Diagnostics) types.Map { - if len(values) == 0 { + if values == nil { return types.MapNull(types.StringType) } diff --git a/internal/resource/infrastructure_stack_schema.go b/internal/resource/infrastructure_stack_schema.go index 1b60c05..32a28c5 100644 --- a/internal/resource/infrastructure_stack_schema.go +++ b/internal/resource/infrastructure_stack_schema.go @@ -6,10 +6,11 @@ import ( "github.com/hashicorp/terraform-plugin-framework-validators/mapvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/mapdefault" "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" @@ -156,7 +157,7 @@ func (r *InfrastructureStackResource) schema() schema.Schema { ElementType: types.StringType, Optional: true, Computed: true, - PlanModifiers: []planmodifier.Map{mapplanmodifier.UseStateForUnknown()}, + Default: mapdefault.StaticValue(types.MapValueMust(types.StringType, map[string]attr.Value{})), Validators: []validator.Map{mapvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("raw"))}, }, "annotations": schema.MapAttribute{ @@ -165,7 +166,7 @@ func (r *InfrastructureStackResource) schema() schema.Schema { ElementType: types.StringType, Optional: true, Computed: true, - PlanModifiers: []planmodifier.Map{mapplanmodifier.UseStateForUnknown()}, + Default: mapdefault.StaticValue(types.MapValueMust(types.StringType, map[string]attr.Value{})), Validators: []validator.Map{mapvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("raw"))}, }, "service_account": schema.StringAttribute{ From 6014ae20155c2474ad766f0be89e3131296b9338 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Wed, 24 Apr 2024 12:28:24 +0200 Subject: [PATCH 56/57] unset computed for files attribute --- internal/resource/infrastructure_stack_schema.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/resource/infrastructure_stack_schema.go b/internal/resource/infrastructure_stack_schema.go index 32a28c5..a5ce790 100644 --- a/internal/resource/infrastructure_stack_schema.go +++ b/internal/resource/infrastructure_stack_schema.go @@ -102,7 +102,6 @@ func (r *InfrastructureStackResource) schema() schema.Schema { Description: "File path-content map.", MarkdownDescription: "File path-content map.", Optional: true, - Computed: true, ElementType: types.StringType, }, "environment": schema.SetNestedAttribute{ From 11d3079e7ea45fb2d9cb61a02118063cd39abc97 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Wed, 24 Apr 2024 13:06:42 +0200 Subject: [PATCH 57/57] fix mapping issues --- internal/common/cluster_bindings.go | 12 ++-- internal/common/map.go | 12 ++++ .../resource/infrastructure_stack_model.go | 70 ++++++++++--------- .../resource/infrastructure_stack_schema.go | 10 --- 4 files changed, 57 insertions(+), 47 deletions(-) diff --git a/internal/common/cluster_bindings.go b/internal/common/cluster_bindings.go index ceac7f1..14e1180 100644 --- a/internal/common/cluster_bindings.go +++ b/internal/common/cluster_bindings.go @@ -56,13 +56,15 @@ func (cb *ClusterBindings) From(readBindings []*console.PolicyBindingFragment, w return } - cb.Read = clusterBindingsFrom(readBindings, ctx, d) - cb.Write = clusterBindingsFrom(writeBindings, ctx, d) + cb.Read = clusterBindingsFrom(readBindings, cb.Read, ctx, d) + cb.Write = clusterBindingsFrom(writeBindings, cb.Write, ctx, d) } -func clusterBindingsFrom(bindings []*console.PolicyBindingFragment, ctx context.Context, d diag.Diagnostics) types.Set { - if bindings == nil { - return types.SetNull(basetypes.ObjectType{AttrTypes: ClusterPolicyBindingAttrTypes}) +func clusterBindingsFrom(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. + return config } values := make([]attr.Value, len(bindings)) diff --git a/internal/common/map.go b/internal/common/map.go index 8e11724..86d2049 100644 --- a/internal/common/map.go +++ b/internal/common/map.go @@ -16,3 +16,15 @@ func MapFrom(values map[string]any, ctx context.Context, d diag.Diagnostics) typ d.Append(diags...) return mapValue } + +func MapFromWithConfig(values map[string]any, config types.Map, ctx context.Context, d diag.Diagnostics) types.Map { + if len(values) == 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. + return config + } + + mapValue, diags := types.MapValueFrom(ctx, types.StringType, values) + d.Append(diags...) + return mapValue +} diff --git a/internal/resource/infrastructure_stack_model.go b/internal/resource/infrastructure_stack_model.go index be33ac8..e4cf801 100644 --- a/internal/resource/infrastructure_stack_model.go +++ b/internal/resource/infrastructure_stack_model.go @@ -89,15 +89,17 @@ func (is *infrastructureStack) From(stack *gqlclient.InfrastructureStackFragment is.ClusterId = types.StringValue(stack.Cluster.ID) is.Repository.From(stack.Repository, stack.Git) is.Configuration.From(stack.Configuration) - is.Files = infrastructureStackFilesFrom(stack.Files, d) - is.Environment = infrastructureStackEnvironmentsFrom(stack.Environment, ctx, d) + is.Files = infrastructureStackFilesFrom(stack.Files, is.Files, d) + is.Environment = infrastructureStackEnvironmentsFrom(stack.Environment, is.Environment, ctx, d) is.Bindings.From(stack.ReadBindings, stack.WriteBindings, ctx, d) is.JobSpec.From(stack.JobSpec, ctx, d) } -func infrastructureStackFilesFrom(files []*gqlclient.StackFileFragment, d diag.Diagnostics) basetypes.MapValue { - if files == nil { - return types.MapNull(types.StringType) +func infrastructureStackFilesFrom(files []*gqlclient.StackFileFragment, config types.Map, d diag.Diagnostics) types.Map { + if len(files) == 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. + return config } resultMap := make(map[string]attr.Value, len(files)) @@ -111,6 +113,29 @@ func infrastructureStackFilesFrom(files []*gqlclient.StackFileFragment, d diag.D return result } +func infrastructureStackEnvironmentsFrom(envs []*gqlclient.StackEnvironmentFragment, config types.Set, ctx context.Context, d diag.Diagnostics) types.Set { + if len(envs) == 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. + return config + } + + values := make([]attr.Value, len(envs)) + for i, file := range envs { + objValue, diags := types.ObjectValueFrom(ctx, InfrastructureStackEnvironmentAttrTypes, InfrastructureStackEnvironment{ + Name: types.StringValue(file.Name), + Value: types.StringValue(file.Value), + Secret: types.BoolPointerValue(file.Secret), + }) + values[i] = objValue + d.Append(diags...) + } + + setValue, diags := types.SetValue(basetypes.ObjectType{AttrTypes: InfrastructureStackEnvironmentAttrTypes}, values) + d.Append(diags...) + return setValue +} + type InfrastructureStackRepository struct { Id types.String `tfsdk:"id"` Ref types.String `tfsdk:"ref"` @@ -180,27 +205,6 @@ var InfrastructureStackEnvironmentAttrTypes = map[string]attr.Type{ "secret": types.BoolType, } -func infrastructureStackEnvironmentsFrom(envs []*gqlclient.StackEnvironmentFragment, ctx context.Context, d diag.Diagnostics) types.Set { - if envs == nil { - return types.SetNull(basetypes.ObjectType{AttrTypes: InfrastructureStackEnvironmentAttrTypes}) - } - - values := make([]attr.Value, len(envs)) - for i, file := range envs { - objValue, diags := types.ObjectValueFrom(ctx, InfrastructureStackEnvironmentAttrTypes, InfrastructureStackEnvironment{ - Name: types.StringValue(file.Name), - Value: types.StringValue(file.Value), - Secret: types.BoolPointerValue(file.Secret), - }) - values[i] = objValue - d.Append(diags...) - } - - setValue, diags := types.SetValue(basetypes.ObjectType{AttrTypes: InfrastructureStackEnvironmentAttrTypes}, values) - d.Append(diags...) - return setValue -} - type InfrastructureStackBindings struct { Read []*InfrastructureStackPolicyBinding `tfsdk:"read"` Write []*InfrastructureStackPolicyBinding `tfsdk:"write"` @@ -279,15 +283,17 @@ func (isjs *InfrastructureStackJobSpec) From(spec *gqlclient.JobGateSpecFragment isjs.Namespace = types.StringValue(spec.Namespace) isjs.Raw = types.StringPointerValue(spec.Raw) - isjs.Containers = infrastructureStackJobSpecContainersFrom(spec.Containers, ctx, d) - isjs.Labels = common.MapFrom(spec.Labels, ctx, d) - isjs.Annotations = common.MapFrom(spec.Annotations, ctx, d) + isjs.Containers = infrastructureStackJobSpecContainersFrom(spec.Containers, isjs.Containers, ctx, d) + isjs.Labels = common.MapFromWithConfig(spec.Labels, isjs.Labels, ctx, d) + isjs.Annotations = common.MapFromWithConfig(spec.Annotations, isjs.Annotations, ctx, d) isjs.ServiceAccount = types.StringPointerValue(spec.ServiceAccount) } -func infrastructureStackJobSpecContainersFrom(containers []*gqlclient.ContainerSpecFragment, ctx context.Context, d diag.Diagnostics) types.Set { - if containers == nil { - return types.SetNull(basetypes.ObjectType{AttrTypes: InfrastructureStackContainerSpecAttrTypes}) +func infrastructureStackJobSpecContainersFrom(containers []*gqlclient.ContainerSpecFragment, config types.Set, ctx context.Context, d diag.Diagnostics) types.Set { + if len(containers) == 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. + return config } values := make([]attr.Value, len(containers)) diff --git a/internal/resource/infrastructure_stack_schema.go b/internal/resource/infrastructure_stack_schema.go index a5ce790..49577bc 100644 --- a/internal/resource/infrastructure_stack_schema.go +++ b/internal/resource/infrastructure_stack_schema.go @@ -6,11 +6,9 @@ import ( "github.com/hashicorp/terraform-plugin-framework-validators/mapvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" - "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/mapdefault" "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" @@ -108,7 +106,6 @@ func (r *InfrastructureStackResource) schema() schema.Schema { Description: "Defines environment variables for the stack.", MarkdownDescription: "Defines environment variables for the stack.", Optional: true, - Computed: true, NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "name": schema.StringAttribute{ @@ -155,8 +152,6 @@ func (r *InfrastructureStackResource) schema() schema.Schema { MarkdownDescription: "Kubernetes labels applied to the job.", ElementType: types.StringType, Optional: true, - Computed: true, - Default: mapdefault.StaticValue(types.MapValueMust(types.StringType, map[string]attr.Value{})), Validators: []validator.Map{mapvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("raw"))}, }, "annotations": schema.MapAttribute{ @@ -164,8 +159,6 @@ func (r *InfrastructureStackResource) schema() schema.Schema { MarkdownDescription: "Kubernetes annotations applied to the job.", ElementType: types.StringType, Optional: true, - Computed: true, - Default: mapdefault.StaticValue(types.MapValueMust(types.StringType, map[string]attr.Value{})), Validators: []validator.Map{mapvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("raw"))}, }, "service_account": schema.StringAttribute{ @@ -176,7 +169,6 @@ func (r *InfrastructureStackResource) schema() schema.Schema { }, "containers": schema.SetNestedAttribute{ Optional: true, - Computed: true, NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "image": schema.StringAttribute{ @@ -226,7 +218,6 @@ func (r *InfrastructureStackResource) schema() schema.Schema { Description: "Read policies of this stack.", MarkdownDescription: "Read policies of this stack.", Optional: true, - Computed: true, NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "group_id": schema.StringAttribute{ @@ -245,7 +236,6 @@ func (r *InfrastructureStackResource) schema() schema.Schema { Description: "Write policies of this stack.", MarkdownDescription: "Write policies of this stack.", Optional: true, - Computed: true, NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "group_id": schema.StringAttribute{