diff --git a/code_generator/provider_code_spec.json b/code_generator/provider_code_spec.json index 04fb5f5..f71f652 100644 --- a/code_generator/provider_code_spec.json +++ b/code_generator/provider_code_spec.json @@ -164,7 +164,7 @@ "name": "role", "string": { "computed_optional_required": "required", - "description": "Team permission. Should be admin, creator, or member.", + "description": "Team role. Should be admin, creator, or member.", "validators": [ { "custom": { @@ -182,6 +182,89 @@ ] } }, + { + "name": "organization_team_permission", + "schema": { + "attributes": [ + { + "name": "orgname", + "string": { + "computed_optional_required": "required", + "description": "Organization name", + "plan_modifiers": [ + { + "custom": { + "imports": [ + { + "path": "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + } + ], + "schema_definition": "stringplanmodifier.RequiresReplace()" + } + } + ] + } + }, + { + "name": "reponame", + "string": { + "computed_optional_required": "required", + "description": "Repository name", + "plan_modifiers": [ + { + "custom": { + "imports": [ + { + "path": "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + } + ], + "schema_definition": "stringplanmodifier.RequiresReplace()" + } + } + ] + } + }, + { + "name": "teamname", + "string": { + "computed_optional_required": "required", + "description": "Team name", + "plan_modifiers": [ + { + "custom": { + "imports": [ + { + "path": "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + } + ], + "schema_definition": "stringplanmodifier.RequiresReplace()" + } + } + ] + } + }, + { + "name": "permission", + "string": { + "computed_optional_required": "required", + "description": "Team permission. Should be read, write or admin.", + "validators": [ + { + "custom": { + "imports": [ + { + "path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + } + ], + "schema_definition": "stringvalidator.OneOf([]string{\"read\", \"write\", \"admin\"}...)" + } + } + ] + } + } + ] + } + }, { "name": "repository", "schema": { @@ -282,6 +365,41 @@ ] } }, + { + "name": "organization_team_permission", + "schema": { + "attributes": [ + { + "name": "orgname", + "string": { + "computed_optional_required": "required", + "description": "Organization name" + } + }, + { + "name": "reponame", + "string": { + "computed_optional_required": "required", + "description": "Repository name" + } + }, + { + "name": "teamname", + "string": { + "computed_optional_required": "required", + "description": "Team name" + } + }, + { + "name": "permission", + "string": { + "computed_optional_required": "computed", + "description": "Team permission" + } + } + ] + } + }, { "name": "repository", "schema": { diff --git a/docs/data-sources/organization_team_permission.md b/docs/data-sources/organization_team_permission.md new file mode 100644 index 0000000..1d1c83e --- /dev/null +++ b/docs/data-sources/organization_team_permission.md @@ -0,0 +1,50 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "quay_organization_team_permission Data Source - quay" +subcategory: "" +description: |- + +--- + +# quay_organization_team_permission (Data Source) + + + +## Example Usage + +```terraform +resource "quay_organization" "org" { + name = "org" + email = "quay+org@example.com" +} + +resource "quay_repository" "repo" { + name = "repo" + namespace = quay_organization.org.name +} + +resource "quay_organization_team" "team" { + name = "team" + orgname = quay_organization.org.name + role = "member" +} + +data "quay_organization_team_permission" "permission" { + orgname = quay_organization.org.name + reponame = quay_repository.repo.name + teamname = quay_organization_team.team.name +} +``` + + +## Schema + +### Required + +- `orgname` (String) Organization name +- `reponame` (String) Repository name +- `teamname` (String) Team name + +### Read-Only + +- `permission` (String) Team permission diff --git a/docs/resources/organization_team.md b/docs/resources/organization_team.md index 896946e..1b0d416 100644 --- a/docs/resources/organization_team.md +++ b/docs/resources/organization_team.md @@ -40,7 +40,7 @@ resource "quay_organization_team" "admin" { - `name` (String) Team name - `orgname` (String) Organization name -- `role` (String) Team permission. Should be admin, creator, or member. +- `role` (String) Team role. Should be admin, creator, or member. ### Optional diff --git a/docs/resources/organization_team_permission.md b/docs/resources/organization_team_permission.md new file mode 100644 index 0000000..c71652c --- /dev/null +++ b/docs/resources/organization_team_permission.md @@ -0,0 +1,57 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "quay_organization_team_permission Resource - quay" +subcategory: "" +description: |- + +--- + +# quay_organization_team_permission (Resource) + + + +## Example Usage + +```terraform +resource "quay_organization" "org" { + name = "org" + email = "quay+org@example.com" +} + +resource "quay_repository" "repo" { + name = "repo" + namespace = quay_organization.org.name +} + +resource "quay_organization_team" "team" { + name = "team" + orgname = quay_organization.org.name + role = "member" +} + +resource "quay_organization_team_permission" "permission" { + orgname = quay_organization.org.name + reponame = quay_repository.repo.name + teamname = quay_organization_team.team.name + permission = "read" +} +``` + + +## Schema + +### Required + +- `orgname` (String) Organization name +- `permission` (String) Team permission. Should be read, write or admin. +- `reponame` (String) Repository name +- `teamname` (String) Team name + +## Import + +Import is supported using the following syntax: + +```shell +# An organization team permission can be imported using the corresponding organization, repository and team name. +terraform import quay_organization_team_permission.permission org repo team +``` diff --git a/examples/data-sources/quay_organization_team_permission/data-source.tf b/examples/data-sources/quay_organization_team_permission/data-source.tf new file mode 100644 index 0000000..112b6af --- /dev/null +++ b/examples/data-sources/quay_organization_team_permission/data-source.tf @@ -0,0 +1,21 @@ +resource "quay_organization" "org" { + name = "org" + email = "quay+org@example.com" +} + +resource "quay_repository" "repo" { + name = "repo" + namespace = quay_organization.org.name +} + +resource "quay_organization_team" "team" { + name = "team" + orgname = quay_organization.org.name + role = "member" +} + +data "quay_organization_team_permission" "permission" { + orgname = quay_organization.org.name + reponame = quay_repository.repo.name + teamname = quay_organization_team.team.name +} diff --git a/examples/resources/quay_organization_team_permission/import.sh b/examples/resources/quay_organization_team_permission/import.sh new file mode 100644 index 0000000..be411ee --- /dev/null +++ b/examples/resources/quay_organization_team_permission/import.sh @@ -0,0 +1,2 @@ +# An organization team permission can be imported using the corresponding organization, repository and team name. +terraform import quay_organization_team_permission.permission org repo team diff --git a/examples/resources/quay_organization_team_permission/resource.tf b/examples/resources/quay_organization_team_permission/resource.tf new file mode 100644 index 0000000..7e92a2b --- /dev/null +++ b/examples/resources/quay_organization_team_permission/resource.tf @@ -0,0 +1,22 @@ +resource "quay_organization" "org" { + name = "org" + email = "quay+org@example.com" +} + +resource "quay_repository" "repo" { + name = "repo" + namespace = quay_organization.org.name +} + +resource "quay_organization_team" "team" { + name = "team" + orgname = quay_organization.org.name + role = "member" +} + +resource "quay_organization_team_permission" "permission" { + orgname = quay_organization.org.name + reponame = quay_repository.repo.name + teamname = quay_organization_team.team.name + permission = "read" +} diff --git a/internal/datasource_organization_team_permission/organization_team_permission_data_source_gen.go b/internal/datasource_organization_team_permission/organization_team_permission_data_source_gen.go new file mode 100644 index 0000000..fc0d291 --- /dev/null +++ b/internal/datasource_organization_team_permission/organization_team_permission_data_source_gen.go @@ -0,0 +1,44 @@ +// Code generated by terraform-plugin-framework-generator DO NOT EDIT. + +package datasource_organization_team_permission + +import ( + "context" + "github.com/hashicorp/terraform-plugin-framework/types" + + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" +) + +func OrganizationTeamPermissionDataSourceSchema(ctx context.Context) schema.Schema { + return schema.Schema{ + Attributes: map[string]schema.Attribute{ + "orgname": schema.StringAttribute{ + Required: true, + Description: "Organization name", + MarkdownDescription: "Organization name", + }, + "permission": schema.StringAttribute{ + Computed: true, + Description: "Team permission", + MarkdownDescription: "Team permission", + }, + "reponame": schema.StringAttribute{ + Required: true, + Description: "Repository name", + MarkdownDescription: "Repository name", + }, + "teamname": schema.StringAttribute{ + Required: true, + Description: "Team name", + MarkdownDescription: "Team name", + }, + }, + } +} + +type OrganizationTeamPermissionModel struct { + Orgname types.String `tfsdk:"orgname"` + Permission types.String `tfsdk:"permission"` + Reponame types.String `tfsdk:"reponame"` + Teamname types.String `tfsdk:"teamname"` +} diff --git a/internal/provider/organization_team_permission_data_source.go b/internal/provider/organization_team_permission_data_source.go new file mode 100644 index 0000000..432111d --- /dev/null +++ b/internal/provider/organization_team_permission_data_source.go @@ -0,0 +1,99 @@ +package provider + +import ( + "context" + "encoding/json" + "fmt" + "io" + "terraform-provider-quay/internal/datasource_organization_team_permission" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/types" + + "github.com/enthought/terraform-provider-quay/quay_api" +) + +var ( + _ datasource.DataSource = (*organizationTeamPermissionDataSource)(nil) + _ datasource.DataSourceWithConfigure = (*organizationTeamPermissionDataSource)(nil) +) + +func NewOrganizationTeamPermissionDataSource() datasource.DataSource { + return &organizationTeamPermissionDataSource{} +} + +type organizationTeamPermissionDataSource struct { + client *quay_api.APIClient +} + +func (d *organizationTeamPermissionDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_organization_team_permission" +} + +func (d *organizationTeamPermissionDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = datasource_organization_team_permission.OrganizationTeamPermissionDataSourceSchema(ctx) +} + +func (d *organizationTeamPermissionDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data datasource_organization_team_permission.OrganizationTeamPermissionModel + var resData teamPermissionModelJSON + + // Read Terraform configuration data into the model + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Create variables + orgName := data.Orgname.ValueString() + repoName := data.Reponame.ValueString() + teamName := data.Teamname.ValueString() + + // Get team permission + httpRes, err := d.client.PermissionAPI.GetTeamPermissions(context.Background(), orgName+"/"+repoName, teamName).Execute() + if err != nil { + errDetail := handleQuayAPIError(err) + resp.Diagnostics.AddError( + "Error reading Quay team permission", + "Could not read Quay team permission, unexpected error: "+errDetail) + return + } + + body, err := io.ReadAll(httpRes.Body) + if err != nil { + resp.Diagnostics.AddError( + "Error reading Quay team permission", + "Could not read Quay team permission, unexpected error: "+err.Error()) + return + } + err = json.Unmarshal(body, &resData) + if err != nil { + resp.Diagnostics.AddError( + "Error reading Quay team permission", + "Could not read Quay team permission, unexpected error: "+err.Error()) + return + } + + // Set permission + data.Permission = types.StringValue(resData.Permission) + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (d *organizationTeamPermissionDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*quay_api.APIClient) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *quay_api.APIClient, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + } + + d.client = client +} diff --git a/internal/provider/organization_team_permission_data_source_test.go b/internal/provider/organization_team_permission_data_source_test.go new file mode 100644 index 0000000..d7a8419 --- /dev/null +++ b/internal/provider/organization_team_permission_data_source_test.go @@ -0,0 +1,58 @@ +package provider + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccOrganizationTeamPermissionDataSource(t *testing.T) { + resource.ParallelTest(t, resource.TestCase{ + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create and Read + { + Config: providerConfig + ` +resource "quay_organization" "org_team_perm_data" { + name = "org_team_perm_data" + email = "quay+org_team_perm_data@example.com" +} + +resource "quay_repository" "test" { + name = "test" + namespace = quay_organization.org_team_perm_data.name +} + +resource "quay_organization_team" "test" { + name = "test" + orgname = quay_organization.org_team_perm_data.name + role = "member" +} + +resource "quay_organization_team_permission" "test" { + orgname = quay_organization.org_team_perm_data.name + reponame = quay_repository.test.name + teamname = quay_organization_team.test.name + permission = "read" +} + +data "quay_organization_team_permission" "test" { + orgname = quay_organization.org_team_perm_data.name + reponame = quay_repository.test.name + teamname = quay_organization_team.test.name + + depends_on = [ + "quay_organization_team_permission.test" + ] +} +`, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.quay_organization_team_permission.test", "orgname", "org_team_perm_data"), + resource.TestCheckResourceAttr("data.quay_organization_team_permission.test", "reponame", "test"), + resource.TestCheckResourceAttr("data.quay_organization_team_permission.test", "teamname", "test"), + resource.TestCheckResourceAttr("data.quay_organization_team_permission.test", "permission", "read"), + ), + }, + }, + }) +} diff --git a/internal/provider/organization_team_permission_resource.go b/internal/provider/organization_team_permission_resource.go new file mode 100644 index 0000000..f587208 --- /dev/null +++ b/internal/provider/organization_team_permission_resource.go @@ -0,0 +1,221 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// Modifications copyright (c) Enthought, Inc. +// SPDX-License-Identifier: BSD-3-Clause + +package provider + +import ( + "context" + "encoding/json" + "fmt" + "io" + "strings" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/types" + + "github.com/enthought/terraform-provider-quay/quay_api" + "terraform-provider-quay/internal/resource_organization_team_permission" +) + +var ( + _ resource.Resource = (*organizationTeamPermissionResource)(nil) + _ resource.ResourceWithConfigure = (*organizationTeamPermissionResource)(nil) + _ resource.ResourceWithImportState = (*organizationTeamPermissionResource)(nil) +) + +func NewOrganizationTeamPermissionResource() resource.Resource { + return &organizationTeamPermissionResource{} +} + +type organizationTeamPermissionResource struct { + client *quay_api.APIClient +} + +type teamPermissionModelJSON struct { + Permission string `json:"role"` +} + +func (r *organizationTeamPermissionResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_organization_team_permission" +} + +func (r *organizationTeamPermissionResource) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = resource_organization_team_permission.OrganizationTeamPermissionResourceSchema(ctx) +} + +func (r *organizationTeamPermissionResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data resource_organization_team_permission.OrganizationTeamPermissionModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Create variables + orgName := data.Orgname.ValueString() + repoName := data.Reponame.ValueString() + teamName := data.Teamname.ValueString() + permission := data.Permission.ValueString() + + // Create team permission + newTeamPermission := quay_api.NewTeamPermission(permission) + _, err := r.client.PermissionAPI.ChangeTeamPermissions(context.Background(), orgName+"/"+repoName, teamName).Body(*newTeamPermission).Execute() + if err != nil { + errDetail := handleQuayAPIError(err) + resp.Diagnostics.AddError( + "Error creating Quay team permission", + "Could not create Quay team permission, unexpected error: "+errDetail) + return + } + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *organizationTeamPermissionResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data resource_organization_team_permission.OrganizationTeamPermissionModel + var resData teamPermissionModelJSON + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Create variables + orgName := data.Orgname.ValueString() + repoName := data.Reponame.ValueString() + teamName := data.Teamname.ValueString() + + // Get team permission + httpRes, err := r.client.PermissionAPI.GetTeamPermissions(context.Background(), orgName+"/"+repoName, teamName).Execute() + if err != nil { + errDetail := handleQuayAPIError(err) + resp.Diagnostics.AddError( + "Error reading Quay team permission", + "Could not read Quay team permission, unexpected error: "+errDetail) + return + } + + body, err := io.ReadAll(httpRes.Body) + if err != nil { + resp.Diagnostics.AddError( + "Error reading Quay team permission", + "Could not read Quay team permission, unexpected error: "+err.Error()) + return + } + err = json.Unmarshal(body, &resData) + if err != nil { + resp.Diagnostics.AddError( + "Error reading Quay team permission", + "Could not read Quay team permission, unexpected error: "+err.Error()) + return + } + + // Set permission + data.Permission = types.StringValue(resData.Permission) + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *organizationTeamPermissionResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var dataState resource_organization_team_permission.OrganizationTeamPermissionModel + var dataPlan resource_organization_team_permission.OrganizationTeamPermissionModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &dataState)...) + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &dataPlan)...) + + if resp.Diagnostics.HasError() { + return + } + + // Create variables + orgName := dataPlan.Orgname.ValueString() + repoName := dataPlan.Reponame.ValueString() + teamName := dataPlan.Teamname.ValueString() + permission := dataPlan.Permission.ValueString() + + // Update team permission + if dataPlan.Permission != dataState.Permission { + newTeamPermission := quay_api.NewTeamPermission(permission) + _, err := r.client.PermissionAPI.ChangeTeamPermissions(context.Background(), orgName+"/"+repoName, teamName).Body(*newTeamPermission).Execute() + if err != nil { + errDetail := handleQuayAPIError(err) + resp.Diagnostics.AddError( + "Error updating Quay team permission", + "Could not update Quay team permission, unexpected error: "+errDetail) + return + } + } + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &dataPlan)...) +} + +func (r *organizationTeamPermissionResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data resource_organization_team_permission.OrganizationTeamPermissionModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Create variables + orgName := data.Orgname.ValueString() + repoName := data.Reponame.ValueString() + teamName := data.Teamname.ValueString() + + // Delete team permission + _, err := r.client.PermissionAPI.DeleteTeamPermissions(context.Background(), orgName+"/"+repoName, teamName).Execute() + if err != nil { + errDetail := handleQuayAPIError(err) + resp.Diagnostics.AddError( + "Error deleting Quay team permission", + "Could not delete Quay team permission, unexpected error: "+errDetail) + return + } +} + +func (r *organizationTeamPermissionResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*quay_api.APIClient) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected *quay_api.APIClient, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + } + + r.client = client +} + +func (r *organizationTeamPermissionResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + idSplit := strings.Split(req.ID, " ") + if len(idSplit) != 3 || idSplit[0] == "" || idSplit[1] == "" || idSplit[2] == "" { + resp.Diagnostics.AddError( + "Unexpected Import Identifier", + fmt.Sprintf("Expected import identifier with format orgname reponame teamname. Got: %q", req.ID), + ) + return + } + + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("orgname"), idSplit[0])...) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("reponame"), idSplit[1])...) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("teamname"), idSplit[2])...) +} diff --git a/internal/provider/organization_team_permission_resource_test.go b/internal/provider/organization_team_permission_resource_test.go new file mode 100644 index 0000000..8bfdf4c --- /dev/null +++ b/internal/provider/organization_team_permission_resource_test.go @@ -0,0 +1,122 @@ +package provider + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccOrganizationTeamPermissionResource(t *testing.T) { + resource.ParallelTest(t, resource.TestCase{ + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create and Read + { + Config: providerConfig + ` +resource "quay_organization" "org_team_perm" { + name = "org_team_perm" + email = "quay+org_team_perm@example.com" +} + +resource "quay_repository" "test" { + name = "test" + namespace = quay_organization.org_team_perm.name +} + +resource "quay_organization_team" "test" { + name = "test" + orgname = quay_organization.org_team_perm.name + role = "member" +} + +resource "quay_organization_team_permission" "test" { + orgname = quay_organization.org_team_perm.name + reponame = quay_repository.test.name + teamname = quay_organization_team.test.name + permission = "read" +} +`, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("quay_organization_team_permission.test", "orgname", "org_team_perm"), + resource.TestCheckResourceAttr("quay_organization_team_permission.test", "reponame", "test"), + resource.TestCheckResourceAttr("quay_organization_team_permission.test", "teamname", "test"), + resource.TestCheckResourceAttr("quay_organization_team_permission.test", "permission", "read"), + ), + }, + // Import + { + ResourceName: "quay_organization_team_permission.test", + ImportState: true, + ImportStateId: "org_team_perm test test", + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: "teamname", + }, + // Update + { + Config: providerConfig + ` +resource "quay_organization" "org_team_perm" { + name = "org_team_perm" + email = "quay+org_team_perm@example.com" +} + +resource "quay_repository" "test" { + name = "test" + namespace = quay_organization.org_team_perm.name +} + +resource "quay_organization_team" "test" { + name = "test" + orgname = quay_organization.org_team_perm.name + role = "member" +} + +resource "quay_organization_team_permission" "test" { + orgname = quay_organization.org_team_perm.name + reponame = quay_repository.test.name + teamname = quay_organization_team.test.name + permission = "write" +} +`, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("quay_organization_team_permission.test", "orgname", "org_team_perm"), + resource.TestCheckResourceAttr("quay_organization_team_permission.test", "reponame", "test"), + resource.TestCheckResourceAttr("quay_organization_team_permission.test", "teamname", "test"), + resource.TestCheckResourceAttr("quay_organization_team_permission.test", "permission", "write"), + ), + }, + // Replace + { + Config: providerConfig + ` +resource "quay_organization" "org_team_perm" { + name = "org_team_perm" + email = "quay+org_team_perm@example.com" +} + +resource "quay_repository" "test2" { + name = "test2" + namespace = quay_organization.org_team_perm.name +} + +resource "quay_organization_team" "test" { + name = "test" + orgname = quay_organization.org_team_perm.name + role = "member" +} + +resource "quay_organization_team_permission" "test" { + orgname = quay_organization.org_team_perm.name + reponame = quay_repository.test2.name + teamname = quay_organization_team.test.name + permission = "write" +} +`, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("quay_organization_team_permission.test", "orgname", "org_team_perm"), + resource.TestCheckResourceAttr("quay_organization_team_permission.test", "reponame", "test2"), + resource.TestCheckResourceAttr("quay_organization_team_permission.test", "teamname", "test"), + resource.TestCheckResourceAttr("quay_organization_team_permission.test", "permission", "write"), + ), + }, + }, + }) +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 2ed585a..d620476 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -152,6 +152,7 @@ func (p *quayProvider) DataSources(_ context.Context) []func() datasource.DataSo return []func() datasource.DataSource{ NewOrganizationDataSource, NewRepositoryDataSource, + NewOrganizationTeamPermissionDataSource, } } @@ -161,5 +162,6 @@ func (p *quayProvider) Resources(_ context.Context) []func() resource.Resource { NewOrganizationTeamResource, NewOrganizationRobotResource, NewRepositoryResource, + NewOrganizationTeamPermissionResource, } } diff --git a/internal/provider/repository_data_source_test.go b/internal/provider/repository_data_source_test.go index 190ba08..55c2bf1 100644 --- a/internal/provider/repository_data_source_test.go +++ b/internal/provider/repository_data_source_test.go @@ -15,7 +15,7 @@ func TestAccRepositoryDataSource(t *testing.T) { Config: providerConfig + ` resource "quay_organization" "org_repo_data" { name = "org_repo_data" - email = "quay+repo@example.com" + email = "quay+org_repo_data@example.com" } resource "quay_repository" "test" { @@ -28,6 +28,10 @@ resource "quay_repository" "test" { data "quay_repository" "test" { name = "test" namespace = quay_organization.org_repo_data.name + + depends_on = [ + quay_repository.test + ] } `, Check: resource.ComposeAggregateTestCheckFunc( diff --git a/internal/resource_organization_team/organization_team_resource_gen.go b/internal/resource_organization_team/organization_team_resource_gen.go index 3cefe9e..22382c4 100644 --- a/internal/resource_organization_team/organization_team_resource_gen.go +++ b/internal/resource_organization_team/organization_team_resource_gen.go @@ -48,8 +48,8 @@ func OrganizationTeamResourceSchema(ctx context.Context) schema.Schema { }, "role": schema.StringAttribute{ Required: true, - Description: "Team permission. Should be admin, creator, or member.", - MarkdownDescription: "Team permission. Should be admin, creator, or member.", + Description: "Team role. Should be admin, creator, or member.", + MarkdownDescription: "Team role. Should be admin, creator, or member.", Validators: []validator.String{ stringvalidator.OneOf([]string{"admin", "creator", "member"}...), }, diff --git a/internal/resource_organization_team_permission/organization_team_permission_resource_gen.go b/internal/resource_organization_team_permission/organization_team_permission_resource_gen.go new file mode 100644 index 0000000..2b00e42 --- /dev/null +++ b/internal/resource_organization_team_permission/organization_team_permission_resource_gen.go @@ -0,0 +1,60 @@ +// Code generated by terraform-plugin-framework-generator DO NOT EDIT. + +package resource_organization_team_permission + +import ( + "context" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "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" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema" +) + +func OrganizationTeamPermissionResourceSchema(ctx context.Context) schema.Schema { + return schema.Schema{ + Attributes: map[string]schema.Attribute{ + "orgname": schema.StringAttribute{ + Required: true, + Description: "Organization name", + MarkdownDescription: "Organization name", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "permission": schema.StringAttribute{ + Required: true, + Description: "Team permission. Should be read, write or admin.", + MarkdownDescription: "Team permission. Should be read, write or admin.", + Validators: []validator.String{ + stringvalidator.OneOf([]string{"read", "write", "admin"}...), + }, + }, + "reponame": schema.StringAttribute{ + Required: true, + Description: "Repository name", + MarkdownDescription: "Repository name", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "teamname": schema.StringAttribute{ + Required: true, + Description: "Team name", + MarkdownDescription: "Team name", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + }, + } +} + +type OrganizationTeamPermissionModel struct { + Orgname types.String `tfsdk:"orgname"` + Permission types.String `tfsdk:"permission"` + Reponame types.String `tfsdk:"reponame"` + Teamname types.String `tfsdk:"teamname"` +} diff --git a/quay_api/test/api_permission_test.go b/quay_api/test/api_permission_test.go index b1e5d9e..ea5ab21 100644 --- a/quay_api/test/api_permission_test.go +++ b/quay_api/test/api_permission_test.go @@ -1,39 +1,61 @@ -/* -Quay Frontend - -Testing PermissionAPIService - -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); - package quay_api import ( "context" - openapiclient "github.com/enthought/terraform-provider-quay/quay_api" + "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "testing" + + "github.com/enthought/terraform-provider-quay/quay_api" ) func Test_quay_api_PermissionAPIService(t *testing.T) { - - configuration := openapiclient.NewConfiguration() - apiClient := openapiclient.NewAPIClient(configuration) + configuration := newConfiguration() + apiClient := quay_api.NewAPIClient(configuration) t.Run("Test PermissionAPIService ChangeTeamPermissions", func(t *testing.T) { - - t.Skip("skip test") // remove to run test - - var repository string - var teamname string - - httpRes, err := apiClient.PermissionAPI.ChangeTeamPermissions(context.Background(), repository, teamname).Execute() - - require.Nil(t, err) + orgName := "change-team-perm" + teamName := "test" + repoName := "test" + + // Ensure org is destroyed + defer func(organization quay_api.ApiDeleteAdminedOrganizationRequest) { + _, err := organization.Execute() + handleQuayAPIError(t, err) + }(apiClient.OrganizationAPI.DeleteAdminedOrganization(context.Background(), orgName)) + + // Create org + newOrg := quay_api.NewNewOrg(orgName, orgName+"@example.com") + httpRes, err := apiClient.OrganizationAPI.CreateOrganization(context.Background()).Body(*newOrg).Execute() + handleQuayAPIError(t, err) + assert.Equal(t, 201, httpRes.StatusCode) + + // Create team + newTeam := quay_api.TeamDescription{ + Role: "member", + Description: nil, + } + httpRes, err = apiClient.TeamAPI.UpdateOrganizationTeam(context.Background(), orgName, teamName).Body(newTeam).Execute() + handleQuayAPIError(t, err) assert.Equal(t, 200, httpRes.StatusCode) + // Create repo + newRepo := quay_api.NewRepo{ + Repository: repoName, + Visibility: "private", + Namespace: &orgName, + Description: "test", + } + httpRes, err = apiClient.RepositoryAPI.CreateRepo(context.Background()).Body(newRepo).Execute() + handleQuayAPIError(t, err) + assert.Equal(t, 201, httpRes.StatusCode) + + // Change team permissions + newTeamPermission := quay_api.NewTeamPermission("read") + httpRes, err = apiClient.PermissionAPI.ChangeTeamPermissions(context.Background(), orgName+"/"+repoName, teamName).Body(*newTeamPermission).Execute() + handleQuayAPIError(t, err) + assert.Equal(t, 200, httpRes.StatusCode) }) t.Run("Test PermissionAPIService ChangeUserPermissions", func(t *testing.T) { @@ -51,17 +73,52 @@ func Test_quay_api_PermissionAPIService(t *testing.T) { }) t.Run("Test PermissionAPIService DeleteTeamPermissions", func(t *testing.T) { + orgName := "delete-team-perm" + teamName := "test" + repoName := "test" + + // Ensure org is destroyed + defer func(organization quay_api.ApiDeleteAdminedOrganizationRequest) { + _, err := organization.Execute() + handleQuayAPIError(t, err) + }(apiClient.OrganizationAPI.DeleteAdminedOrganization(context.Background(), orgName)) + + // Create org + newOrg := quay_api.NewNewOrg(orgName, orgName+"@example.com") + httpRes, err := apiClient.OrganizationAPI.CreateOrganization(context.Background()).Body(*newOrg).Execute() + handleQuayAPIError(t, err) + assert.Equal(t, 201, httpRes.StatusCode) + + // Create team + newTeam := quay_api.TeamDescription{ + Role: "member", + Description: nil, + } + httpRes, err = apiClient.TeamAPI.UpdateOrganizationTeam(context.Background(), orgName, teamName).Body(newTeam).Execute() + handleQuayAPIError(t, err) + assert.Equal(t, 200, httpRes.StatusCode) - t.Skip("skip test") // remove to run test - - var repository string - var teamname string - - httpRes, err := apiClient.PermissionAPI.DeleteTeamPermissions(context.Background(), repository, teamname).Execute() - - require.Nil(t, err) + // Create repo + newRepo := quay_api.NewRepo{ + Repository: repoName, + Visibility: "private", + Namespace: &orgName, + Description: "test", + } + httpRes, err = apiClient.RepositoryAPI.CreateRepo(context.Background()).Body(newRepo).Execute() + handleQuayAPIError(t, err) + assert.Equal(t, 201, httpRes.StatusCode) + + // Change team permissions + newTeamPermission := quay_api.NewTeamPermission("read") + httpRes, err = apiClient.PermissionAPI.ChangeTeamPermissions(context.Background(), orgName+"/"+repoName, teamName).Body(*newTeamPermission).Execute() + handleQuayAPIError(t, err) assert.Equal(t, 200, httpRes.StatusCode) + // Delete team permissions + httpRes, err = apiClient.PermissionAPI.DeleteTeamPermissions(context.Background(), orgName+"/"+repoName, teamName).Execute() + handleQuayAPIError(t, err) + assert.Equal(t, 204, httpRes.StatusCode) }) t.Run("Test PermissionAPIService DeleteUserPermissions", func(t *testing.T) { @@ -79,17 +136,52 @@ func Test_quay_api_PermissionAPIService(t *testing.T) { }) t.Run("Test PermissionAPIService GetTeamPermissions", func(t *testing.T) { + orgName := "get-team-perm" + teamName := "test" + repoName := "test" + + // Ensure org is destroyed + defer func(organization quay_api.ApiDeleteAdminedOrganizationRequest) { + _, err := organization.Execute() + handleQuayAPIError(t, err) + }(apiClient.OrganizationAPI.DeleteAdminedOrganization(context.Background(), orgName)) + + // Create org + newOrg := quay_api.NewNewOrg(orgName, orgName+"@example.com") + httpRes, err := apiClient.OrganizationAPI.CreateOrganization(context.Background()).Body(*newOrg).Execute() + handleQuayAPIError(t, err) + assert.Equal(t, 201, httpRes.StatusCode) + + // Create team + newTeam := quay_api.TeamDescription{ + Role: "member", + Description: nil, + } + httpRes, err = apiClient.TeamAPI.UpdateOrganizationTeam(context.Background(), orgName, teamName).Body(newTeam).Execute() + handleQuayAPIError(t, err) + assert.Equal(t, 200, httpRes.StatusCode) - t.Skip("skip test") // remove to run test - - var repository string - var teamname string - - httpRes, err := apiClient.PermissionAPI.GetTeamPermissions(context.Background(), repository, teamname).Execute() - - require.Nil(t, err) + // Create repo + newRepo := quay_api.NewRepo{ + Repository: repoName, + Visibility: "private", + Namespace: &orgName, + Description: "test", + } + httpRes, err = apiClient.RepositoryAPI.CreateRepo(context.Background()).Body(newRepo).Execute() + handleQuayAPIError(t, err) + assert.Equal(t, 201, httpRes.StatusCode) + + // Change team permissions + newTeamPermission := quay_api.NewTeamPermission("read") + httpRes, err = apiClient.PermissionAPI.ChangeTeamPermissions(context.Background(), orgName+"/"+repoName, teamName).Body(*newTeamPermission).Execute() + handleQuayAPIError(t, err) assert.Equal(t, 200, httpRes.StatusCode) + // Get team permissions + httpRes, err = apiClient.PermissionAPI.GetTeamPermissions(context.Background(), orgName+"/"+repoName, teamName).Execute() + handleQuayAPIError(t, err) + assert.Equal(t, 200, httpRes.StatusCode) }) t.Run("Test PermissionAPIService GetUserPermissions", func(t *testing.T) {