From 1554d46f0d2a0fedd019411e77c97295f15675d5 Mon Sep 17 00:00:00 2001 From: xescugc Date: Mon, 18 Mar 2024 14:17:24 +0100 Subject: [PATCH] provider/catalog_repository: Added the CRUD for the Catalog Repository --- Makefile | 4 + docs/resources/catalog_repository.md | 33 +++ generator_config.yml | 19 ++ out_code_spec.json | 141 ++++++++++++ provider/catalog_repository_resource.go | 202 ++++++++++++++++++ provider/credential_resource.go | 2 - provider/provider.go | 1 + .../catalog_repository_resource_gen.go | 74 +++++++ 8 files changed, 474 insertions(+), 2 deletions(-) create mode 100644 docs/resources/catalog_repository.md create mode 100644 provider/catalog_repository_resource.go create mode 100644 resource_catalog_repository/catalog_repository_resource_gen.go diff --git a/Makefile b/Makefile index 15474fc..60e8851 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,10 @@ tf-generate: ## Will regenerate the new provider spec and models @tfplugingen-openapi generate --config generator_config.yml --output out_code_spec.json openapi.yaml @tfplugingen-framework generate resources --input ./out_code_spec.json --output . +.PHONY: new-resource +new-resource: ## Generates boilplate code for new resource R + @tfplugingen-framework scaffold resource --name $(R) --output-dir ./provider + .PHONY: install install: ## Install the provider @go install . diff --git a/docs/resources/catalog_repository.md b/docs/resources/catalog_repository.md new file mode 100644 index 0000000..f836a1b --- /dev/null +++ b/docs/resources/catalog_repository.md @@ -0,0 +1,33 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "cycloid_catalog_repository Resource - terraform-provider-cycloid" +subcategory: "" +description: |- + +--- + +# cycloid_catalog_repository (Resource) + + + + + + +## Schema + +### Required + +- `branch` (String) +- `name` (String) +- `url` (String) + +### Optional + +- `canonical` (String) +- `credential_canonical` (String) +- `organization_canonical` (String) A canonical of an organization. +- `owner` (String) User canonical that owns this service catalog source. If omitted then the person +creating this service catalog source will be assigned as owner. When a user is the +owner of a service catalog source they has all the permissions on it. + + diff --git a/generator_config.yml b/generator_config.yml index 50a8725..6d2b24e 100644 --- a/generator_config.yml +++ b/generator_config.yml @@ -33,3 +33,22 @@ resources: ignores: - data - credential_canonical + catalog_repository: + create: + path: /organizations/{organization_canonical}/service_catalog_sources + method: POST + read: + path: /organizations/{organization_canonical}/service_catalog_sources/{service_catalog_source_canonical} + method: GET + update: + path: /organizations/{organization_canonical}/service_catalog_sources/{service_catalog_source_canonical} + method: PUT + delete: + path: /organizations/{organization_canonical}/service_catalog_sources/{service_catalog_source_canonical} + method: DELETE + schema: + ignores: + - data + - service_catalog_source_canonical + - page_index + - page_size diff --git a/out_code_spec.json b/out_code_spec.json index 214162d..39dab1b 100644 --- a/out_code_spec.json +++ b/out_code_spec.json @@ -25,6 +25,147 @@ } }, "resources": [ + { + "name": "catalog_repository", + "schema": { + "attributes": [ + { + "name": "branch", + "string": { + "computed_optional_required": "required" + } + }, + { + "name": "canonical", + "string": { + "computed_optional_required": "computed_optional", + "validators": [ + { + "custom": { + "imports": [ + { + "path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + } + ], + "schema_definition": "stringvalidator.LengthBetween(3, 100)" + } + }, + { + "custom": { + "imports": [ + { + "path": "regexp" + }, + { + "path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + } + ], + "schema_definition": "stringvalidator.RegexMatches(regexp.MustCompile(\"^[a-z0-9]+[a-z0-9\\\\-_]+[a-z0-9]+$\"), \"\")" + } + } + ] + } + }, + { + "name": "credential_canonical", + "string": { + "computed_optional_required": "computed_optional", + "validators": [ + { + "custom": { + "imports": [ + { + "path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + } + ], + "schema_definition": "stringvalidator.LengthBetween(3, 100)" + } + }, + { + "custom": { + "imports": [ + { + "path": "regexp" + }, + { + "path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + } + ], + "schema_definition": "stringvalidator.RegexMatches(regexp.MustCompile(\"^[a-z0-9]+[a-z0-9\\\\-_]+[a-z0-9]+$\"), \"\")" + } + } + ] + } + }, + { + "name": "name", + "string": { + "computed_optional_required": "required" + } + }, + { + "name": "owner", + "string": { + "computed_optional_required": "computed_optional", + "description": "User canonical that owns this service catalog source. If omitted then the person\ncreating this service catalog source will be assigned as owner. When a user is the\nowner of a service catalog source they has all the permissions on it.\n" + } + }, + { + "name": "url", + "string": { + "computed_optional_required": "required", + "validators": [ + { + "custom": { + "imports": [ + { + "path": "regexp" + }, + { + "path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + } + ], + "schema_definition": "stringvalidator.RegexMatches(regexp.MustCompile(\"^((/|~)[^/]*)+.(\\\\.git)|(([\\\\w\\\\]+@[\\\\w\\\\.]+))(:(//)?)([\\\\w\\\\.@\\\\:/\\\\-~]+)(/)?\"), \"\")" + } + } + ] + } + }, + { + "name": "organization_canonical", + "string": { + "computed_optional_required": "computed_optional", + "description": "A canonical of an organization.", + "validators": [ + { + "custom": { + "imports": [ + { + "path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + } + ], + "schema_definition": "stringvalidator.LengthBetween(3, 100)" + } + }, + { + "custom": { + "imports": [ + { + "path": "regexp" + }, + { + "path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + } + ], + "schema_definition": "stringvalidator.RegexMatches(regexp.MustCompile(\"^[a-z0-9]+[a-z0-9\\\\-_]+[a-z0-9]+$\"), \"\")" + } + } + ] + } + } + ] + } + }, { "name": "credential", "schema": { diff --git a/provider/catalog_repository_resource.go b/provider/catalog_repository_resource.go new file mode 100644 index 0000000..33d09f6 --- /dev/null +++ b/provider/catalog_repository_resource.go @@ -0,0 +1,202 @@ +package provider + +import ( + "context" + + "github.com/cycloidio/cycloid-cli/client/models" + "github.com/cycloidio/cycloid-cli/cmd/cycloid/common" + "github.com/cycloidio/cycloid-cli/cmd/cycloid/middleware" + "github.com/cycloidio/terraform-provider-cycloid/provider_cycloid" + "github.com/cycloidio/terraform-provider-cycloid/resource_catalog_repository" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" +) + +var _ resource.Resource = (*catalogRepositoryResource)(nil) + +func NewCatalogRepositoryResource() resource.Resource { + return &catalogRepositoryResource{} +} + +type catalogRepositoryResource struct { + provider provider_cycloid.CycloidModel +} + +type catalogRepositoryResourceModel resource_catalog_repository.CatalogRepositoryModel + +func (r *catalogRepositoryResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_catalog_repository" +} + +func (r *catalogRepositoryResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = resource_catalog_repository.CatalogRepositoryResourceSchema(ctx) +} + +func (r *catalogRepositoryResource) Configure(ctx context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + pv, ok := req.ProviderData.(provider_cycloid.CycloidModel) + if !ok { + tflog.Error(ctx, "Unable to prepare client") + return + } + r.provider = pv +} + +func (r *catalogRepositoryResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data catalogRepositoryResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Create API call logic + api := common.NewAPI(common.WithURL(r.provider.Url.ValueString()), common.WithToken(r.provider.Jwt.ValueString())) + mid := middleware.NewMiddleware(api) + + orgCan := getOrganizationCanonical(r.provider, data.OrganizationCanonical) + name := data.Name.ValueString() + url := data.Url.ValueString() + branch := data.Branch.ValueString() + credCan := data.CredentialCanonical.ValueString() + + cr, err := mid.CreateCatalogRepository(orgCan, name, url, branch, credCan) + if err != nil { + resp.Diagnostics.AddError( + "Unable create catalog repository", + err.Error(), + ) + return + } + + catalogRepositoryCYModelToData(orgCan, cr, &data) + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *catalogRepositoryResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data catalogRepositoryResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Read API call logic + api := common.NewAPI(common.WithURL(r.provider.Url.ValueString()), common.WithToken(r.provider.Jwt.ValueString())) + mid := middleware.NewMiddleware(api) + + can := data.Canonical.ValueString() + + orgCan := getOrganizationCanonical(r.provider, data.OrganizationCanonical) + + cr, err := mid.GetCatalogRepository(orgCan, can) + if err != nil { + resp.Diagnostics.AddError( + "Unable read catalog repository", + err.Error(), + ) + return + } + + catalogRepositoryCYModelToData(orgCan, cr, &data) + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *catalogRepositoryResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var data catalogRepositoryResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Update API call logic + api := common.NewAPI(common.WithURL(r.provider.Url.ValueString()), common.WithToken(r.provider.Jwt.ValueString())) + mid := middleware.NewMiddleware(api) + + orgCan := getOrganizationCanonical(r.provider, data.OrganizationCanonical) + name := data.Name.ValueString() + url := data.Url.ValueString() + branch := data.Branch.ValueString() + credCan := data.CredentialCanonical.ValueString() + can := data.Canonical.ValueString() + + if can == "" { + var plandata catalogRepositoryResourceModel + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &plandata)...) + if resp.Diagnostics.HasError() { + return + } + can = plandata.Canonical.ValueString() + } + + cr, err := mid.UpdateCatalogRepository(orgCan, can, name, url, branch, credCan) + if err != nil { + resp.Diagnostics.AddError( + "Unable update catalog repository", + err.Error(), + ) + return + } + + catalogRepositoryCYModelToData(orgCan, cr, &data) + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *catalogRepositoryResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data catalogRepositoryResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Delete API call logic + api := common.NewAPI(common.WithURL(r.provider.Url.ValueString()), common.WithToken(r.provider.Jwt.ValueString())) + mid := middleware.NewMiddleware(api) + + can := data.Canonical.ValueString() + orgCan := getOrganizationCanonical(r.provider, data.OrganizationCanonical) + + err := mid.DeleteCatalogRepository(orgCan, can) + if err != nil { + resp.Diagnostics.AddError( + "Unable delete catalog repository", + err.Error(), + ) + return + } +} + +func catalogRepositoryCYModelToData(org string, cr *models.ServiceCatalogSource, data *catalogRepositoryResourceModel) diag.Diagnostics { + var diags diag.Diagnostics + + data.Name = types.StringPointerValue(cr.Name) + data.Url = types.StringPointerValue(cr.URL) + data.Branch = types.StringValue(cr.Branch) + data.Canonical = types.StringPointerValue(cr.Canonical) + data.Owner = types.StringPointerValue(cr.Owner.Username) + data.OrganizationCanonical = types.StringValue(org) + + return diags +} diff --git a/provider/credential_resource.go b/provider/credential_resource.go index 4520ae2..dc12e08 100644 --- a/provider/credential_resource.go +++ b/provider/credential_resource.go @@ -9,7 +9,6 @@ import ( "github.com/cycloidio/cycloid-cli/cmd/cycloid/middleware" "github.com/cycloidio/terraform-provider-cycloid/provider_cycloid" "github.com/cycloidio/terraform-provider-cycloid/resource_credential" - "github.com/davecgh/go-spew/spew" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/types" @@ -170,7 +169,6 @@ func (r *credentialResource) Update(ctx context.Context, req resource.UpdateRequ } orgCan := getOrganizationCanonical(r.provider, data.OrganizationCanonical) - spew.Dump(data.OrganizationCanonical) cred, err := mid.UpdateCredential(orgCan, name, ct, rawCred, path, can, des) if err != nil { diff --git a/provider/provider.go b/provider/provider.go index 7a28560..b67459c 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -49,5 +49,6 @@ func (p *cycloidProvider) Resources(ctx context.Context) []func() resource.Resou return []func() resource.Resource{ NewOrganizationResource, NewCredentialResource, + NewCatalogRepositoryResource, } } diff --git a/resource_catalog_repository/catalog_repository_resource_gen.go b/resource_catalog_repository/catalog_repository_resource_gen.go new file mode 100644 index 0000000..da58e2f --- /dev/null +++ b/resource_catalog_repository/catalog_repository_resource_gen.go @@ -0,0 +1,74 @@ +// Code generated by terraform-plugin-framework-generator DO NOT EDIT. + +package resource_catalog_repository + +import ( + "context" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "regexp" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema" +) + +func CatalogRepositoryResourceSchema(ctx context.Context) schema.Schema { + return schema.Schema{ + Attributes: map[string]schema.Attribute{ + "branch": schema.StringAttribute{ + Required: true, + }, + "canonical": schema.StringAttribute{ + Optional: true, + Computed: true, + Validators: []validator.String{ + stringvalidator.LengthBetween(3, 100), + stringvalidator.RegexMatches(regexp.MustCompile("^[a-z0-9]+[a-z0-9\\-_]+[a-z0-9]+$"), ""), + }, + }, + "credential_canonical": schema.StringAttribute{ + Optional: true, + Computed: true, + Validators: []validator.String{ + stringvalidator.LengthBetween(3, 100), + stringvalidator.RegexMatches(regexp.MustCompile("^[a-z0-9]+[a-z0-9\\-_]+[a-z0-9]+$"), ""), + }, + }, + "name": schema.StringAttribute{ + Required: true, + }, + "organization_canonical": schema.StringAttribute{ + Optional: true, + Computed: true, + Description: "A canonical of an organization.", + MarkdownDescription: "A canonical of an organization.", + Validators: []validator.String{ + stringvalidator.LengthBetween(3, 100), + stringvalidator.RegexMatches(regexp.MustCompile("^[a-z0-9]+[a-z0-9\\-_]+[a-z0-9]+$"), ""), + }, + }, + "owner": schema.StringAttribute{ + Optional: true, + Computed: true, + Description: "User canonical that owns this service catalog source. If omitted then the person\ncreating this service catalog source will be assigned as owner. When a user is the\nowner of a service catalog source they has all the permissions on it.\n", + MarkdownDescription: "User canonical that owns this service catalog source. If omitted then the person\ncreating this service catalog source will be assigned as owner. When a user is the\nowner of a service catalog source they has all the permissions on it.\n", + }, + "url": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile("^((/|~)[^/]*)+.(\\.git)|(([\\w\\]+@[\\w\\.]+))(:(//)?)([\\w\\.@\\:/\\-~]+)(/)?"), ""), + }, + }, + }, + } +} + +type CatalogRepositoryModel struct { + Branch types.String `tfsdk:"branch"` + Canonical types.String `tfsdk:"canonical"` + CredentialCanonical types.String `tfsdk:"credential_canonical"` + Name types.String `tfsdk:"name"` + OrganizationCanonical types.String `tfsdk:"organization_canonical"` + Owner types.String `tfsdk:"owner"` + Url types.String `tfsdk:"url"` +}