diff --git a/examples/resources/biganimal_faraway_replica/import.sh b/examples/resources/biganimal_faraway_replica/import.sh new file mode 100644 index 00000000..f7a48516 --- /dev/null +++ b/examples/resources/biganimal_faraway_replica/import.sh @@ -0,0 +1,2 @@ +# terraform import biganimal_faraway_replica. / +terraform import biganimal_faraway_replica.faraway_replica prj_deadbeef01234567/p-abcd123456 diff --git a/pkg/api/tag_client.go b/pkg/api/tag_client.go index 0264be45..0d5cadd4 100644 --- a/pkg/api/tag_client.go +++ b/pkg/api/tag_client.go @@ -101,3 +101,14 @@ func (tc TagClient) List(ctx context.Context) ([]api.TagResponse, error) { return response.Data, err } + +func (tc TagClient) Delete(ctx context.Context, tagId string) error { + url := fmt.Sprintf("tags/%s", tagId) + + _, err := tc.doRequest(ctx, http.MethodDelete, url, nil) + if err != nil { + return err + } + + return nil +} diff --git a/pkg/provider/data_source_fareplica.go b/pkg/provider/data_source_fareplica.go index 35edff44..662d7426 100644 --- a/pkg/provider/data_source_fareplica.go +++ b/pkg/provider/data_source_fareplica.go @@ -254,6 +254,23 @@ func (c *FAReplicaData) Schema(ctx context.Context, req datasource.SchemaRequest MarkdownDescription: "Transparent data encryption action.", Computed: true, }, + "tags": schema.SetNestedAttribute{ + Description: "Assign existing tags or create tags to assign to this resource", + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "tag_id": schema.StringAttribute{ + Computed: true, + }, + "tag_name": schema.StringAttribute{ + Required: true, + }, + "color": schema.StringAttribute{ + Computed: true, + }, + }, + }, + }, }, } } diff --git a/pkg/provider/resource_cluster.go b/pkg/provider/resource_cluster.go index 0d645e94..6238c56c 100644 --- a/pkg/provider/resource_cluster.go +++ b/pkg/provider/resource_cluster.go @@ -1153,6 +1153,16 @@ func (c *clusterResource) generateGenericClusterModel(ctx context.Context, clust } } + tags := []commonApi.Tag{} + for _, tag := range clusterResource.Tags { + tags = append(tags, commonApi.Tag{ + Color: tag.Color.ValueStringPointer(), + TagId: tag.TagId.ValueString(), + TagName: tag.TagName.ValueString(), + }) + } + cluster.Tags = tags + svAccIds, principalIds, err := c.buildRequestBah(ctx, clusterResource) if err != nil { return models.Cluster{}, err diff --git a/pkg/provider/resource_fareplica.go b/pkg/provider/resource_fareplica.go index f38be0da..09c9f9b6 100644 --- a/pkg/provider/resource_fareplica.go +++ b/pkg/provider/resource_fareplica.go @@ -9,6 +9,8 @@ import ( "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/constants" "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/models" + commonApi "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/models/common/api" + commonTerraform "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/models/common/terraform" "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/plan_modifier" "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/utils" "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" @@ -62,6 +64,7 @@ type FAReplicaResourceModel struct { PgIdentity types.String `tfsdk:"pg_identity"` TransparentDataEncryptionAction types.String `tfsdk:"transparent_data_encryption_action"` VolumeSnapshot types.Bool `tfsdk:"volume_snapshot_backup"` + Tags []commonTerraform.Tag `tfsdk:"tags"` Timeouts timeouts.Value `tfsdk:"timeouts"` } @@ -318,8 +321,8 @@ func (r *FAReplicaResource) Schema(ctx context.Context, req resource.SchemaReque Computed: true, Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ - Description: "Cluster architecture ID. For example, \"single\" or \"ha\".For Extreme High Availability clusters, please use the [biganimal_pgd](https://registry.terraform.io/providers/EnterpriseDB/biganimal/latest/docs/resources/pgd) resource.", - Computed: true, + Description: "Cluster architecture ID.", + Required: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, }, "name": schema.StringAttribute{ @@ -329,10 +332,11 @@ func (r *FAReplicaResource) Schema(ctx context.Context, req resource.SchemaReque }, "nodes": schema.Float64Attribute{ Description: "Node count.", - Computed: true, + Required: true, PlanModifiers: []planmodifier.Float64{float64planmodifier.UseStateForUnknown()}, }, }, + PlanModifiers: []planmodifier.Object{objectplanmodifier.UseStateForUnknown()}, }, "pg_version": schema.StringAttribute{ MarkdownDescription: "Postgres version. See [Supported Postgres types and versions](https://www.enterprisedb.com/docs/biganimal/latest/overview/05_database_version_policy/#supported-postgres-types-and-versions) for supported Postgres types and versions.", @@ -687,6 +691,15 @@ func readFAReplica(ctx context.Context, client *api.ClusterClient, fAReplicaReso fAReplicaResourceModel.TransparentDataEncryption.Status = types.StringValue(responseCluster.EncryptionKeyResp.Status) } + fAReplicaResourceModel.Tags = []commonTerraform.Tag{} + for _, v := range responseCluster.Tags { + fAReplicaResourceModel.Tags = append(fAReplicaResourceModel.Tags, commonTerraform.Tag{ + TagId: types.StringValue(v.TagId), + TagName: types.StringValue(v.TagName), + Color: basetypes.NewStringPointerValue(v.Color), + }) + } + return nil } @@ -788,6 +801,16 @@ func (r *FAReplicaResource) generateGenericFAReplicaModel(ctx context.Context, f } } + tags := []commonApi.Tag{} + for _, tag := range fAReplicaResourceModel.Tags { + tags = append(tags, commonApi.Tag{ + Color: tag.Color.ValueStringPointer(), + TagId: tag.TagId.ValueString(), + TagName: tag.TagName.ValueString(), + }) + } + cluster.Tags = tags + return cluster, nil } diff --git a/pkg/provider/resource_pgd.go b/pkg/provider/resource_pgd.go index fc8e9ae5..a9b11cdc 100644 --- a/pkg/provider/resource_pgd.go +++ b/pkg/provider/resource_pgd.go @@ -1265,8 +1265,8 @@ func buildTFGroupsAs(ctx context.Context, diags *diag.Diagnostics, state tfsdk.S switch apiGroupResp := v.(type) { case map[string]interface{}: if apiGroupResp["clusterType"] == "witness_group" { - apiWgModel := pgdApi.WitnessGroup{} - if err := utils.CopyObjectJson(apiGroupResp, &apiWgModel); err != nil { + apiRespWgModel := pgdApi.WitnessGroup{} + if err := utils.CopyObjectJson(apiGroupResp, &apiRespWgModel); err != nil { diags.AddError("unable to copy witness group", err.Error()) return } @@ -1280,9 +1280,9 @@ func buildTFGroupsAs(ctx context.Context, diags *diag.Diagnostics, state tfsdk.S switch apiGroupResp := v.(type) { case map[string]interface{}: if apiGroupResp["clusterType"] == "data_group" { - apiDgModel := pgdApi.DataGroup{} + apiRespDgModel := pgdApi.DataGroup{} - if err := utils.CopyObjectJson(apiGroupResp, &apiDgModel); err != nil { + if err := utils.CopyObjectJson(apiGroupResp, &apiRespDgModel); err != nil { diags.AddError("unable to copy data group", err.Error()) return } @@ -1292,25 +1292,25 @@ func buildTFGroupsAs(ctx context.Context, diags *diag.Diagnostics, state tfsdk.S // cluster arch clusterArch := &terraform.ClusterArchitecture{ - ClusterArchitectureId: apiDgModel.ClusterArchitecture.ClusterArchitectureId, - ClusterArchitectureName: types.StringPointerValue(apiDgModel.ClusterArchitecture.ClusterArchitectureName), - Nodes: apiDgModel.ClusterArchitecture.Nodes, - WitnessNodes: types.Int64Value(int64(*apiDgModel.ClusterArchitecture.WitnessNodes)), + ClusterArchitectureId: apiRespDgModel.ClusterArchitecture.ClusterArchitectureId, + ClusterArchitectureName: types.StringPointerValue(apiRespDgModel.ClusterArchitecture.ClusterArchitectureName), + Nodes: apiRespDgModel.ClusterArchitecture.Nodes, + WitnessNodes: types.Int64Value(int64(*apiRespDgModel.ClusterArchitecture.WitnessNodes)), } // pgConfig. If tf resource pg config elem matches with api response pg config elem then add the elem to tf resource pg config newPgConfig := []models.KeyValue{} var tfPgConfig *[]models.KeyValue for _, pgdTFResourceDG := range originalTFDgs { - if pgdTFResourceDG.Region.RegionId == apiDgModel.Region.RegionId { + if pgdTFResourceDG.Region.RegionId == apiRespDgModel.Region.RegionId { tfPgConfig = pgdTFResourceDG.PgConfig break } } - if tfPgConfig != nil && apiDgModel.PgConfig != nil { + if tfPgConfig != nil && apiRespDgModel.PgConfig != nil { for _, tfPgConf := range *tfPgConfig { - for _, apiPgConf := range *apiDgModel.PgConfig { + for _, apiPgConf := range *apiRespDgModel.PgConfig { if tfPgConf.Name == apiPgConf.Name { newPgConfig = append(newPgConfig, models.KeyValue{Name: apiPgConf.Name, Value: apiPgConf.Value}) } @@ -1320,8 +1320,8 @@ func buildTFGroupsAs(ctx context.Context, diags *diag.Diagnostics, state tfsdk.S // resizing pvc resizingPvc := []attr.Value{} - if apiDgModel.ResizingPvc != nil && len(*apiDgModel.ResizingPvc) != 0 { - for _, v := range *apiDgModel.ResizingPvc { + if apiRespDgModel.ResizingPvc != nil && len(*apiRespDgModel.ResizingPvc) != 0 { + for _, v := range *apiRespDgModel.ResizingPvc { v := v resizingPvc = append(resizingPvc, types.StringPointerValue(&v)) } @@ -1329,17 +1329,17 @@ func buildTFGroupsAs(ctx context.Context, diags *diag.Diagnostics, state tfsdk.S // storage storage := &terraform.Storage{ - Size: types.StringPointerValue(apiDgModel.Storage.Size), - VolumePropertiesId: types.StringPointerValue(apiDgModel.Storage.VolumePropertiesId), - VolumeTypeId: types.StringPointerValue(apiDgModel.Storage.VolumeTypeId), - Iops: types.StringPointerValue(apiDgModel.Storage.Iops), - Throughput: types.StringPointerValue(apiDgModel.Storage.Throughput), + Size: types.StringPointerValue(apiRespDgModel.Storage.Size), + VolumePropertiesId: types.StringPointerValue(apiRespDgModel.Storage.VolumePropertiesId), + VolumeTypeId: types.StringPointerValue(apiRespDgModel.Storage.VolumeTypeId), + Iops: types.StringPointerValue(apiRespDgModel.Storage.Iops), + Throughput: types.StringPointerValue(apiRespDgModel.Storage.Throughput), } // service account ids serviceAccIds := []attr.Value{} - if apiDgModel.ServiceAccountIds != nil && len(*apiDgModel.ServiceAccountIds) != 0 { - for _, v := range *apiDgModel.ServiceAccountIds { + if apiRespDgModel.ServiceAccountIds != nil && len(*apiRespDgModel.ServiceAccountIds) != 0 { + for _, v := range *apiRespDgModel.ServiceAccountIds { v := v serviceAccIds = append(serviceAccIds, types.StringPointerValue(&v)) } @@ -1347,8 +1347,8 @@ func buildTFGroupsAs(ctx context.Context, diags *diag.Diagnostics, state tfsdk.S // pe allowed principal ids account ids principalIds := []attr.Value{} - if apiDgModel.PeAllowedPrincipalIds != nil && len(*apiDgModel.PeAllowedPrincipalIds) != 0 { - for _, v := range *apiDgModel.PeAllowedPrincipalIds { + if apiRespDgModel.PeAllowedPrincipalIds != nil && len(*apiRespDgModel.PeAllowedPrincipalIds) != 0 { + for _, v := range *apiRespDgModel.PeAllowedPrincipalIds { v := v principalIds = append(principalIds, types.StringPointerValue(&v)) } @@ -1372,8 +1372,8 @@ func buildTFGroupsAs(ctx context.Context, diags *diag.Diagnostics, state tfsdk.S return } allowedIpRanges := []attr.Value{} - if apiDgModel.AllowedIpRanges != nil && len(*apiDgModel.AllowedIpRanges) > 0 { - for _, v := range *apiDgModel.AllowedIpRanges { + if apiRespDgModel.AllowedIpRanges != nil && len(*apiRespDgModel.AllowedIpRanges) > 0 { + for _, v := range *apiRespDgModel.AllowedIpRanges { v := v ob, diag := types.ObjectValue(allwdIpRngsElemTFType.AttrTypes, map[string]attr.Value{ "cidr_block": types.StringValue(v.CidrBlock), @@ -1394,32 +1394,32 @@ func buildTFGroupsAs(ctx context.Context, diags *diag.Diagnostics, state tfsdk.S } tfDGModel := terraform.DataGroup{ - GroupId: types.StringPointerValue(apiDgModel.GroupId), + GroupId: types.StringPointerValue(apiRespDgModel.GroupId), AllowedIpRanges: allwdIpRngsSet, - BackupRetentionPeriod: apiDgModel.BackupRetentionPeriod, + BackupRetentionPeriod: apiRespDgModel.BackupRetentionPeriod, ClusterArchitecture: clusterArch, - ClusterName: types.StringPointerValue(apiDgModel.ClusterName), - ClusterType: types.StringPointerValue(apiDgModel.ClusterType), - Connection: types.StringPointerValue((*string)(apiDgModel.Connection)), - CreatedAt: types.StringPointerValue((*string)(apiDgModel.CreatedAt)), - CspAuth: apiDgModel.CspAuth, - InstanceType: apiDgModel.InstanceType, - LogsUrl: types.StringPointerValue(apiDgModel.LogsUrl), - MetricsUrl: types.StringPointerValue(apiDgModel.MetricsUrl), + ClusterName: types.StringPointerValue(apiRespDgModel.ClusterName), + ClusterType: types.StringPointerValue(apiRespDgModel.ClusterType), + Connection: types.StringPointerValue((*string)(apiRespDgModel.Connection)), + CreatedAt: types.StringPointerValue((*string)(apiRespDgModel.CreatedAt)), + CspAuth: apiRespDgModel.CspAuth, + InstanceType: apiRespDgModel.InstanceType, + LogsUrl: types.StringPointerValue(apiRespDgModel.LogsUrl), + MetricsUrl: types.StringPointerValue(apiRespDgModel.MetricsUrl), PgConfig: &newPgConfig, - PgType: apiDgModel.PgType, - PgVersion: apiDgModel.PgVersion, - Phase: types.StringPointerValue(apiDgModel.Phase), - PrivateNetworking: apiDgModel.PrivateNetworking, - Provider: apiDgModel.Provider, - Region: apiDgModel.Region, + PgType: apiRespDgModel.PgType, + PgVersion: apiRespDgModel.PgVersion, + Phase: types.StringPointerValue(apiRespDgModel.Phase), + PrivateNetworking: apiRespDgModel.PrivateNetworking, + Provider: apiRespDgModel.Provider, + Region: apiRespDgModel.Region, ResizingPvc: types.SetValueMust(types.StringType, resizingPvc), Storage: storage, - MaintenanceWindow: apiDgModel.MaintenanceWindow, + MaintenanceWindow: apiRespDgModel.MaintenanceWindow, ServiceAccountIds: types.SetValueMust(types.StringType, serviceAccIds), PeAllowedPrincipalIds: types.SetValueMust(types.StringType, principalIds), - RoConnectionUri: types.StringPointerValue(apiDgModel.RoConnectionUri), - ReadOnlyConnections: apiDgModel.ReadOnlyConnections, + RoConnectionUri: types.StringPointerValue(apiRespDgModel.RoConnectionUri), + ReadOnlyConnections: apiRespDgModel.ReadOnlyConnections, } outPgdTFResource.DataGroups = append(outPgdTFResource.DataGroups, tfDGModel) diff --git a/pkg/provider/resource_tag.go b/pkg/provider/resource_tag.go index 34e1b048..aafc454d 100644 --- a/pkg/provider/resource_tag.go +++ b/pkg/provider/resource_tag.go @@ -168,6 +168,20 @@ func (tr *tagResource) Update(ctx context.Context, req resource.UpdateRequest, r } func (tr *tagResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state TagResourceModel + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + err := tr.client.Delete(ctx, state.TagId.ValueString()) + if err != nil { + if !appendDiagFromBAErr(err, &resp.Diagnostics) { + resp.Diagnostics.AddError("Error deleting tag", err.Error()) + } + return + } } func (tr *tagResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {