Skip to content

Commit

Permalink
feat: tag assign project (#533)
Browse files Browse the repository at this point in the history
* feat: resource tag create

* fix: lint errors

* fix: lint errors

* refactor: refactor tag client

* feat: tag update

* refactor: refactor tag client

* feat: changed tag description

* feat: tag import

* feat: lint error

* feat: assign tags to resources

* fix: faraway replica assign tag

* feat: tag in cluster examples

* fix: plan modifier for cluster to merge plan with state so it can deal with existing tags when planning

* fix: cluster assign tags fix

* feat: pgd assign tags

* fix: lint fix

* fix: examples

* feat: project tags assign

* fix: planmodifier to use state so it can assign and remove correctly

* fix: custom plan modifier

* fix: commends on build assign tags
  • Loading branch information
wai-wong-edb authored Oct 3, 2024
1 parent dbfbf6a commit 92bc3af
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 41 deletions.
9 changes: 9 additions & 0 deletions examples/resources/biganimal_project/resource.tf
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ resource "random_pet" "project_name" {

resource "biganimal_project" "this" {
project_name = format("TF %s", title(random_pet.project_name.id))
#tags = [
# {
# tag_name = "ex-tag-name-1"
# color = "blue"
# },
# {
# tag_name = "ex-tag-name-2"
# },
#]
}

output "project_name" {
Expand Down
17 changes: 13 additions & 4 deletions pkg/api/project_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,18 @@ func NewProjectClient(api API) *ProjectClient {
return &c
}

func (c ProjectClient) Create(ctx context.Context, projectName string) (string, error) {
func (c ProjectClient) Create(ctx context.Context, model any) (string, error) {
response := struct {
Data struct {
ProjectId string `json:"projectId"`
} `json:"data"`
}{}

project := map[string]string{"projectName": projectName}
projectRs := model.(models.Project)
project := map[string]interface{}{
"projectName": projectRs.ProjectName,
"tags": projectRs.Tags,
}

b, err := json.Marshal(project)
if err != nil {
Expand Down Expand Up @@ -83,14 +87,19 @@ func (c ProjectClient) List(ctx context.Context, query string) ([]*models.Projec
return response.Data, err
}

func (c ProjectClient) Update(ctx context.Context, projectId, projectName string) (string, error) {
func (c ProjectClient) Update(ctx context.Context, projectId, model any) (string, error) {
response := struct {
Data struct {
ProjectId string `json:"projectId"`
} `json:"data"`
}{}

project := map[string]string{"projectName": projectName}
projectRs := model.(models.Project)
project := map[string]interface{}{
"projectName": projectRs.ProjectName,
"tags": projectRs.Tags,
}

b, err := json.Marshal(project)
if err != nil {
return "", err
Expand Down
3 changes: 3 additions & 0 deletions pkg/models/project.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package models

import commonApi "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/models/common/api"

type CloudProvider struct {
CloudProviderId string `json:"cloudProviderId,omitempty" tfsdk:"cloud_provider_id"`
CloudProviderName string `json:"cloudProviderName,omitempty" tfsdk:"cloud_provider_name"`
Expand All @@ -11,6 +13,7 @@ type Project struct {
UserCount int `json:"userCount,omitempty" tfsdk:"user_count"`
ClusterCount int `json:"clusterCount,omitempty" tfsdk:"cluster_count"`
CloudProviders []CloudProvider `json:"cloudProviders" tfsdk:"cloud_providers"`
Tags []commonApi.Tag `json:"tags,omitempty"`
}

// Check the return value, if ProjectName is also needed
Expand Down
6 changes: 3 additions & 3 deletions pkg/plan_modifier/assign_tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ func (m assignTagsModifier) PlanModifySet(ctx context.Context, req planmodifier.
return
}

// This is on update and tags are not set in config so just plan for state
// Below is everything else ie update with tags set in config.

// This is on update and tags are not set in config so just use state as plan
if req.PlanValue.IsUnknown() {
resp.PlanValue = basetypes.NewSetValueMust(req.ConfigValue.ElementType(ctx), state.Elements())
return
}

// This is for anything else ie update with tags set in config.

// merge plan into newPlan (plan is from config) and merge state in newPlan (state is from read)
newPlan := state.Elements()

Expand Down
33 changes: 33 additions & 0 deletions pkg/provider/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package provider

import (
commonApi "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/models/common/api"
commonTerraform "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/models/common/terraform"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
)

// build tag assign terraform resource as, using api response as input
func buildTFRsrcAssignTagsAs(tfRsrcTagsOut *[]commonTerraform.Tag, apiRespTags []commonApi.Tag) {
*tfRsrcTagsOut = []commonTerraform.Tag{}
for _, v := range apiRespTags {
*tfRsrcTagsOut = append(*tfRsrcTagsOut, commonTerraform.Tag{
TagId: types.StringValue(v.TagId),
TagName: types.StringValue(v.TagName),
Color: basetypes.NewStringPointerValue(v.Color),
})
}
}

// build tag assign request using terraform resource as input
func buildAPIReqAssignTags(tfRsrcTags []commonTerraform.Tag) []commonApi.Tag {
tags := []commonApi.Tag{}
for _, tag := range tfRsrcTags {
tags = append(tags, commonApi.Tag{
Color: tag.Color.ValueStringPointer(),
TagId: tag.TagId.ValueString(),
TagName: tag.TagName.ValueString(),
})
}
return tags
}
25 changes: 1 addition & 24 deletions pkg/provider/resource_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -963,7 +963,7 @@ func readCluster(ctx context.Context, client *api.ClusterClient, tfClusterResour
}
}

buildTFRsrcAssignTagsAs(&tfClusterResource.Tags, &responseCluster.Tags)
buildTFRsrcAssignTagsAs(&tfClusterResource.Tags, responseCluster.Tags)

if responseCluster.EncryptionKeyResp != nil {
tfClusterResource.TransparentDataEncryption = &TransparentDataEncryptionModel{}
Expand Down Expand Up @@ -1221,26 +1221,3 @@ func StringSliceToSet(items *[]string) types.Set {

return types.SetValueMust(types.StringType, eles)
}

func buildTFRsrcAssignTagsAs(tfRsrcTags *[]commonTerraform.Tag, apiRespTags *[]commonApi.Tag) {
*tfRsrcTags = []commonTerraform.Tag{}
for _, v := range *apiRespTags {
*tfRsrcTags = append(*tfRsrcTags, commonTerraform.Tag{
TagId: types.StringValue(v.TagId),
TagName: types.StringValue(v.TagName),
Color: basetypes.NewStringPointerValue(v.Color),
})
}
}

func buildAPIReqAssignTags(tfRsrcTags []commonTerraform.Tag) []commonApi.Tag {
tags := []commonApi.Tag{}
for _, tag := range tfRsrcTags {
tags = append(tags, commonApi.Tag{
Color: tag.Color.ValueStringPointer(),
TagId: tag.TagId.ValueString(),
TagName: tag.TagName.ValueString(),
})
}
return tags
}
4 changes: 2 additions & 2 deletions pkg/provider/resource_pgd.go
Original file line number Diff line number Diff line change
Expand Up @@ -857,7 +857,7 @@ func (p pgdResource) Read(ctx context.Context, req resource.ReadRequest, resp *r
state.ClusterId = clusterResp.ClusterId
state.ClusterName = clusterResp.ClusterName

buildTFRsrcAssignTagsAs(&state.Tags, &clusterResp.Tags)
buildTFRsrcAssignTagsAs(&state.Tags, clusterResp.Tags)

buildTFGroupsAs(ctx, &resp.Diagnostics, resp.State, *clusterResp, &state)
if resp.Diagnostics.HasError() {
Expand Down Expand Up @@ -1190,7 +1190,7 @@ func (p *pgdResource) retryFuncAs(ctx context.Context, diags *diag.Diagnostics,
return retry.RetryableError(errors.New("instance not yet ready"))
}

buildTFRsrcAssignTagsAs(&outPgdTfResource.Tags, &pgdResp.Tags)
buildTFRsrcAssignTagsAs(&outPgdTfResource.Tags, pgdResp.Tags)

return nil
}
Expand Down
66 changes: 58 additions & 8 deletions pkg/provider/resource_project.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import (
"github.com/hashicorp/terraform-plugin-framework/path"

"github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api"
"github.com/EnterpriseDB/terraform-provider-biganimal/pkg/models"
commonTerraform "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/models/common/terraform"
"github.com/EnterpriseDB/terraform-provider-biganimal/pkg/plan_modifier"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
Expand Down Expand Up @@ -94,6 +97,36 @@ func (p projectResource) Schema(ctx context.Context, req resource.SchemaRequest,
},
},
},
"tags": schema.SetNestedAttribute{
Description: "Assign existing tags or create tags to assign to this resource",
Optional: true,
Computed: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"tag_id": schema.StringAttribute{
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"tag_name": schema.StringAttribute{
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"color": schema.StringAttribute{
Optional: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
},
},
PlanModifiers: []planmodifier.Set{
plan_modifier.CustomAssignTags(),
},
},
},
}
}
Expand All @@ -113,12 +146,13 @@ type cloudProvider struct {
}

type Project struct {
ID *string `tfsdk:"id"`
ProjectID *string `tfsdk:"project_id"`
ProjectName *string `tfsdk:"project_name"`
UserCount *int `tfsdk:"user_count"`
ClusterCount *int `tfsdk:"cluster_count"`
CloudProviders []cloudProvider `tfsdk:"cloud_providers"`
ID *string `tfsdk:"id"`
ProjectID *string `tfsdk:"project_id"`
ProjectName *string `tfsdk:"project_name"`
UserCount *int `tfsdk:"user_count"`
ClusterCount *int `tfsdk:"cluster_count"`
CloudProviders []cloudProvider `tfsdk:"cloud_providers"`
Tags []commonTerraform.Tag `tfsdk:"tags"`
}

// Create creates the resource and sets the initial Terraform state.
Expand All @@ -130,7 +164,12 @@ func (p projectResource) Create(ctx context.Context, req resource.CreateRequest,
return
}

projectId, err := p.client.Create(ctx, *config.ProjectName)
projectReqModel := models.Project{
ProjectName: *config.ProjectName,
Tags: buildAPIReqAssignTags(config.Tags),
}

projectId, err := p.client.Create(ctx, projectReqModel)
if err != nil {
resp.Diagnostics.AddError("Error creating project", "Could not create project, unexpected error: "+err.Error())
return
Expand All @@ -154,6 +193,8 @@ func (p projectResource) Create(ctx context.Context, req resource.CreateRequest,
})
}

buildTFRsrcAssignTagsAs(&config.Tags, project.Tags)

diags = resp.State.Set(ctx, &config)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
Expand Down Expand Up @@ -188,6 +229,8 @@ func (p projectResource) Read(ctx context.Context, req resource.ReadRequest, res
})
}

buildTFRsrcAssignTagsAs(&state.Tags, project.Tags)

diags = resp.State.Set(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
Expand All @@ -204,12 +247,19 @@ func (p projectResource) Update(ctx context.Context, req resource.UpdateRequest,
return
}

_, err := p.client.Update(ctx, *plan.ProjectID, *plan.ProjectName)
projectReqModel := models.Project{
ProjectName: *plan.ProjectName,
Tags: buildAPIReqAssignTags(plan.Tags),
}

_, err := p.client.Update(ctx, *plan.ProjectID, projectReqModel)
if err != nil {
resp.Diagnostics.AddError("Error updating project", "Could not update project, unexpected error: "+err.Error())
return
}

buildTFRsrcAssignTagsAs(&plan.Tags, projectReqModel.Tags)

diags = resp.State.Set(ctx, &plan)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
Expand Down

0 comments on commit 92bc3af

Please sign in to comment.