teamcity/role.go (351 lines of code) (raw):

package teamcity import ( "context" "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" "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" "github.com/hashicorp/terraform-plugin-framework/types" "terraform-provider-teamcity/client" ) var ( _ resource.Resource = &roleResource{} _ resource.ResourceWithConfigure = &roleResource{} _ resource.ResourceWithImportState = &roleResource{} ) type roleResource struct { client *client.Client } func NewRoleResource() resource.Resource { return &roleResource{} } func (r *roleResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { resp.TypeName = req.ProviderTypeName + "_role" } type roleResourceModel struct { Name types.String `tfsdk:"name"` Id types.String `tfsdk:"id"` Included types.Set `tfsdk:"included"` Permissions types.Set `tfsdk:"permissions"` } func (r *roleResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ Description: "A role is a set of permissions that can be granted to a user in one or all projects thus controlling access to the projects and various features. More details [here](https://www.jetbrains.com/help/teamcity/managing-roles-and-permissions.html#Managing+Roles)", Attributes: map[string]schema.Attribute{ "name": schema.StringAttribute{ Required: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplace(), }, }, "id": schema.StringAttribute{ Computed: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.UseStateForUnknown(), }, }, "included": schema.SetAttribute{ ElementType: types.StringType, Optional: true, }, "permissions": schema.SetAttribute{ ElementType: types.StringType, Optional: true, Validators: []validator.Set{ setvalidator.ValueStringsAre(stringvalidator.OneOf([]string{ "administer_agent", "administer_agent_for_project", "archive_project", "assign_investigation", "assign_users_add_subgroups", "authorize_agent", "authorize_agent_for_project", "backup", "cancel_any_personal_build", "cancel_build", "change_agent_run_configuration_policy", "change_agent_run_configuration_policy_for_project", "change_cleanup_rules", "change_https_settings", //"change_own_profile", "change_server_settings", "change_user", "change_user_notifications", "change_user_notifications_in_project", "change_user_roles_in_project", "change_usergroup", "change_vcs_username_in_project", "clean_agent_sources", "clean_build_configuration_sources", "comment_build", "configure_server_data_cleanup", "connect_to_agent", "create_delete_vcs_root", "create_sub_project", "create_user", "create_usergroup", "customize_build_parameters", "customize_build_revisions", "delete_sub_project", "delete_user", "delete_usergroup", "edit_enforced_settings", "edit_project", "edit_vcs_modification", "edit_versioned_settings", "enable_disable_agent", "enable_disable_agent_for_project", "import_projects", "label_build", "manage_agent_clouds", "manage_agent_pools", "manage_agent_pools_for_project", //"manage_authentication_settings", "manage_build_problem_instances", "manage_build_problems", //"manage_custom_ssl_certificates", "manage_experimental_features", "manage_roles", //"manage_server_installation", "manage_server_licenses", "patch_build_sources", "pause_activate_build_configuration", "pin_unpin_build", "remove_agent", "remove_agent_for_project", "remove_build", "reorder_build_queue", "run_build", "start_stop_cloud_agent", "tag_build", "view_agent_clouds", "view_agent_details", "view_agent_details_for_project", "view_agent_usage_statistics", "view_all_users", "view_audit_log", "view_build_configuration_settings", "view_build_runtime_data", "view_file_content", "view_project", "view_server_errors", //"view_server_settings", "view_usage_statistics", "view_user_profile", }...)), }, }, }, } } func (r *roleResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { if req.ProviderData == nil { return } r.client = req.ProviderData.(*client.Client) } func (r *roleResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { var plan roleResourceModel diags := req.Plan.Get(ctx, &plan) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } val := plan.Name.ValueString() role := client.Role{ Name: &val, } if !plan.Included.IsNull() { var roles []types.String diags = plan.Included.ElementsAs(ctx, &roles, false) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } role.Included = &client.Included{} for _, i := range roles { val := i.ValueString() role.Included.Role = append( role.Included.Role, client.Role{Id: &val}, ) } } if !plan.Permissions.IsNull() { var perms []types.String diags = plan.Permissions.ElementsAs(ctx, &perms, false) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } role.Permissions = &client.Permissions{} for _, i := range perms { role.Permissions.Permission = append( role.Permissions.Permission, client.Permission{Id: i.ValueString()}, ) } } actual, err := r.client.NewRole(role) if err != nil { resp.Diagnostics.AddError( "Error setting role", "Cannot set role, unexpected error: "+err.Error(), ) return } newState := r.readState(actual) diags = resp.State.Set(ctx, newState) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } } func (r *roleResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { var state roleResourceModel diags := req.State.Get(ctx, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } actual, err := r.client.GetRole(state.Id.ValueString()) if err != nil { resp.Diagnostics.AddError( "Error Reading role", "Could not read role settings: "+err.Error(), ) return } newState := r.readState(actual) diags = resp.State.Set(ctx, newState) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } } func (r *roleResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { var plan roleResourceModel diags := req.Plan.Get(ctx, &plan) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } var oldState roleResourceModel diags = req.State.Get(ctx, &oldState) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } var newState roleResourceModel var stateIncluded []types.String oldState.Included.ElementsAs(ctx, &stateIncluded, false) var planIncluded []types.String plan.Included.ElementsAs(ctx, &planIncluded, false) // items present in old state but missing in a plan -> remove for _, i := range stateIncluded { if !contains(planIncluded, i) { actual, err := r.client.RemoveIncludedRole(plan.Id.ValueString(), i.ValueString()) if err != nil { resp.Diagnostics.AddError( "Error removing included role", "Unexpected error: "+err.Error(), ) return } newState = r.readState(actual) } } // items missing in old state but present in a plan -> add for _, i := range planIncluded { if !contains(stateIncluded, i) { actual, err := r.client.AddIncludedRole(plan.Id.ValueString(), i.ValueString()) if err != nil { resp.Diagnostics.AddError( "Error adding included role", "Unexpected error: "+err.Error(), ) return } newState = r.readState(actual) } } var statePerms []types.String oldState.Permissions.ElementsAs(ctx, &statePerms, false) var planPerms []types.String plan.Permissions.ElementsAs(ctx, &planPerms, false) // items present in old state but missing in a plan -> remove for _, i := range statePerms { if !contains(planPerms, i) { actual, err := r.client.RemovePermission(plan.Id.ValueString(), i.ValueString()) if err != nil { resp.Diagnostics.AddError( "Error removing permission", "Unexpected error: "+err.Error(), ) return } newState = r.readState(actual) } } // items missing in old state but present in a plan -> add for _, i := range planPerms { if !contains(statePerms, i) { actual, err := r.client.AddPermission(plan.Id.ValueString(), i.ValueString()) if err != nil { resp.Diagnostics.AddError( "Error adding permission", "Unexpected error: "+err.Error(), ) return } newState = r.readState(actual) } } diags = resp.State.Set(ctx, newState) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } } func contains(items []types.String, item types.String) bool { for _, i := range items { if i.Equal(item) { return true } } return false } func (r *roleResource) readState(actual client.Role) roleResourceModel { var newState roleResourceModel newState.Name = types.StringValue(*actual.Name) newState.Id = types.StringValue(*actual.Id) newState.Included = types.SetNull(types.StringType) for _, i := range actual.Included.Role { newState.Included, _ = types.SetValue( types.StringType, append(newState.Included.Elements(), types.StringValue(*i.Id)), ) } newState.Permissions = types.SetNull(types.StringType) for _, i := range actual.Permissions.Permission { newState.Permissions, _ = types.SetValue( types.StringType, append(newState.Permissions.Elements(), types.StringValue(i.Id)), ) } return newState } func (r *roleResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { var state roleResourceModel diags := req.State.Get(ctx, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } err := r.client.DeleteRole(state.Id.ValueString()) if err != nil { resp.Diagnostics.AddError( "Error Deleting role", "Could not delete role, unexpected error: "+err.Error(), ) return } } func (r *roleResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) }