diff --git a/docs/resources/role.md b/docs/resources/role.md new file mode 100644 index 00000000..c15843f0 --- /dev/null +++ b/docs/resources/role.md @@ -0,0 +1,60 @@ +--- +page_title: "spectrocloud_role Resource - terraform-provider-spectrocloud" +subcategory: "" +description: |- + The role resource allows you to manage roles in Palette. +--- + +# spectrocloud_role (Resource) + + The role resource allows you to manage roles in Palette. + +You can learn more about managing roles in Palette by reviewing the [Roles](https://docs.spectrocloud.com/glossary-all/#role) guide. + +## Example Usage + +```terraform +variable "roles" { + type = list(string) + default = ["Cluster Admin", "Cluster Profile Editor"] +} + +# Data source loop to retrieve multiple roles +data "spectrocloud_role" "roles" { + for_each = toset(var.roles) + name = each.key +} + +resource "spectrocloud_role" "custom_role" { + name = "Test Cluster Role" + type = "project" + permissions = flatten([for role in data.spectrocloud_role.roles : role.permissions]) +} +``` + + + +## Schema + +### Required + +- `name` (String) The name of the role. +- `permissions` (Set of String) The permission's assigned to the role. + +### Optional + +- `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts)) +- `type` (String) The role type. Allowed values are `project` or `tenant` or `project` + +### Read-Only + +- `id` (String) The ID of this resource. + + +### Nested Schema for `timeouts` + +Optional: + +- `create` (String) +- `delete` (String) +- `update` (String) \ No newline at end of file diff --git a/examples/resources/spectrocloud_role/providers.tf b/examples/resources/spectrocloud_role/providers.tf new file mode 100644 index 00000000..f3bdb2e0 --- /dev/null +++ b/examples/resources/spectrocloud_role/providers.tf @@ -0,0 +1,28 @@ +terraform { + required_providers { + spectrocloud = { + version = ">= 0.1" + source = "spectrocloud/spectrocloud" + } + } +} + +variable "sc_host" { + description = "Spectro Cloud Endpoint" + default = "api.spectrocloud.com" +} + +variable "sc_api_key" { + description = "Spectro Cloud API key" +} + +variable "sc_project_name" { + description = "Spectro Cloud Project (e.g: Default)" + default = "Default" +} + +provider "spectrocloud" { + host = var.sc_host + api_key = var.sc_api_key + project_name = var.sc_project_name +} diff --git a/examples/resources/spectrocloud_role/resource.tf b/examples/resources/spectrocloud_role/resource.tf new file mode 100644 index 00000000..9e00feec --- /dev/null +++ b/examples/resources/spectrocloud_role/resource.tf @@ -0,0 +1,16 @@ +variable "roles" { + type = list(string) + default = ["Cluster Admin", "Cluster Profile Editor"] +} + +# Data source loop to retrieve multiple roles +data "spectrocloud_role" "roles" { + for_each = toset(var.roles) + name = each.key +} + +resource "spectrocloud_role" "custom_role" { + name = "Test Cluster Role" + type = "project" + permissions = flatten([for role in data.spectrocloud_role.roles : role.permissions]) +} \ No newline at end of file diff --git a/examples/resources/spectrocloud_role/terraform.template.tfvars b/examples/resources/spectrocloud_role/terraform.template.tfvars new file mode 100644 index 00000000..c7e9d50b --- /dev/null +++ b/examples/resources/spectrocloud_role/terraform.template.tfvars @@ -0,0 +1,4 @@ +# Spectro Cloud credentials +sc_host = "{Enter Spectro Cloud API Host}" #e.g: api.spectrocloud.com (for SaaS) +sc_api_key = "{Enter Spectro Cloud API Key}" +sc_project_name = "{Enter Spectro Cloud Project Name}" #e.g: Default \ No newline at end of file diff --git a/go.mod b/go.mod index 14d068e3..7cfd1c7d 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/robfig/cron v1.2.0 github.com/spectrocloud/gomi v1.14.1-0.20240214074114-c19394812368 github.com/spectrocloud/hapi v1.14.1-0.20240214071352-81f589b1d86d - github.com/spectrocloud/palette-sdk-go v0.0.0-20241113133445-a5e87250e68d + github.com/spectrocloud/palette-sdk-go v0.0.0-20241113152438-58866fb1d5b7 github.com/stretchr/testify v1.9.0 gotest.tools v2.2.0+incompatible k8s.io/api v0.23.5 diff --git a/go.sum b/go.sum index e7538b97..ec9d8910 100644 --- a/go.sum +++ b/go.sum @@ -600,8 +600,8 @@ github.com/spectrocloud/gomi v1.14.1-0.20240214074114-c19394812368 h1:eY0BOyEbGu github.com/spectrocloud/gomi v1.14.1-0.20240214074114-c19394812368/go.mod h1:LlZ9We4kDaELYi7Is0SVmnySuDhwphJLS6ZT4wXxFIk= github.com/spectrocloud/hapi v1.14.1-0.20240214071352-81f589b1d86d h1:OMRbHxMJ1a+G1BYzvUYuMM0wLkYJPdnEOFx16faQ/UY= github.com/spectrocloud/hapi v1.14.1-0.20240214071352-81f589b1d86d/go.mod h1:MktpRPnSXDTHsQrFSD+daJFQ1zMLSR+1gWOL31jVvWE= -github.com/spectrocloud/palette-sdk-go v0.0.0-20241113133445-a5e87250e68d h1:RkU8p4K15zpH1FB2roV3yrpLiKn+/FcRxuxyJrXmtsk= -github.com/spectrocloud/palette-sdk-go v0.0.0-20241113133445-a5e87250e68d/go.mod h1:dSlNvDS0qwUWTbrYI6P8x981mcbbRHFrBg67v5zl81U= +github.com/spectrocloud/palette-sdk-go v0.0.0-20241113152438-58866fb1d5b7 h1:6qWLXVkq5Ry4tOt1pALAlEz4no8i5XS4SxB8IZEYq6k= +github.com/spectrocloud/palette-sdk-go v0.0.0-20241113152438-58866fb1d5b7/go.mod h1:dSlNvDS0qwUWTbrYI6P8x981mcbbRHFrBg67v5zl81U= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= diff --git a/spectrocloud/provider.go b/spectrocloud/provider.go index 2433011e..4925ab7d 100644 --- a/spectrocloud/provider.go +++ b/spectrocloud/provider.go @@ -140,6 +140,7 @@ func New(_ string) func() *schema.Provider { "spectrocloud_workspace": resourceWorkspace(), "spectrocloud_alert": resourceAlert(), "spectrocloud_ssh_key": resourceSSHKey(), + "spectrocloud_role": resourceRole(), }, DataSourcesMap: map[string]*schema.Resource{ "spectrocloud_user": dataSourceUser(), diff --git a/spectrocloud/resource_role.go b/spectrocloud/resource_role.go new file mode 100644 index 00000000..e7cea279 --- /dev/null +++ b/spectrocloud/resource_role.go @@ -0,0 +1,148 @@ +package spectrocloud + +import ( + "context" + "fmt" + "github.com/spectrocloud/palette-sdk-go/api/models" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceRole() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceRoleCreate, + ReadContext: resourceRoleRead, + UpdateContext: resourceRoleUpdate, + DeleteContext: resourceRoleDelete, + Description: "The role resource allows you to manage roles in Palette.", + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(10 * time.Minute), + Update: schema.DefaultTimeout(10 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + }, + SchemaVersion: 2, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: "The name of the role.", + }, + "type": { + Type: schema.TypeString, + Optional: true, + Default: "project", + ValidateFunc: validation.StringInSlice([]string{"project", "tenant", "resource"}, false), + Description: "The role type. Allowed values are `project` or `tenant` or `project`", + }, + "permissions": { + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: "The permission's assigned to the role.", + }, + }, + } +} + +func convertInterfaceSliceToStringSlice(input []interface{}) ([]string, error) { + var output []string + for _, item := range input { + str, ok := item.(string) + if !ok { + return nil, fmt.Errorf("item %v is not a string", item) + } + output = append(output, str) + } + return output, nil +} + +func toRole(d *schema.ResourceData) *models.V1Role { + name := d.Get("name").(string) + roleType := d.Get("type").(string) + permission, _ := convertInterfaceSliceToStringSlice(d.Get("permissions").(*schema.Set).List()) + return &models.V1Role{ + Metadata: &models.V1ObjectMeta{ + Annotations: map[string]string{ + "scope": roleType, + }, + LastModifiedTimestamp: models.V1Time{}, + Name: name, + }, + Spec: &models.V1RoleSpec{ + Permissions: permission, + Scope: models.V1Scope(roleType), + Type: "user", + }, + Status: &models.V1RoleStatus{ + IsEnabled: true, + }, + } +} + +func flattenRole(d *schema.ResourceData, role *models.V1Role) error { + var err error + err = d.Set("name", role.Metadata.Name) + if err != nil { + return err + } + err = d.Set("type", role.Spec.Scope) + if err != nil { + return err + } + err = d.Set("permissions", role.Spec.Permissions) + if err != nil { + return err + } + return nil +} + +func resourceRoleCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := getV1ClientWithResourceContext(m, "tenant") + var diags diag.Diagnostics + role := toRole(d) + uid, err := c.CreateRole(role) + if err != nil { + return diag.FromErr(err) + } + d.SetId(uid) + return diags +} + +func resourceRoleRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := getV1ClientWithResourceContext(m, "tenant") + var diags diag.Diagnostics + role, err := c.GetRoleByID(d.Id()) + if err != nil { + return diag.FromErr(err) + } + err = flattenRole(d, role) + if err != nil { + return diag.FromErr(err) + } + return diags +} + +func resourceRoleUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := getV1ClientWithResourceContext(m, "tenant") + var diags diag.Diagnostics + role := toRole(d) + err := c.UpdateRole(role, d.Id()) + if err != nil { + return diag.FromErr(err) + } + return diags +} + +func resourceRoleDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := getV1ClientWithResourceContext(m, "tenant") + var diags diag.Diagnostics + err := c.DeleteRole(d.Id()) + if err != nil { + return diag.FromErr(err) + } + return diags +} diff --git a/templates/resources/role.md.tmpl b/templates/resources/role.md.tmpl new file mode 100644 index 00000000..60fcf3d7 --- /dev/null +++ b/templates/resources/role.md.tmpl @@ -0,0 +1,36 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} + +You can learn more about managing roles in Palette by reviewing the [Roles](https://docs.spectrocloud.com/glossary-all/#role) guide. + +## Example Usage + +```terraform +variable "roles" { + type = list(string) + default = ["Cluster Admin", "Cluster Profile Editor"] +} + +# Data source loop to retrieve multiple roles +data "spectrocloud_role" "roles" { + for_each = toset(var.roles) + name = each.key +} + +resource "spectrocloud_role" "custom_role" { + name = "Test Cluster Role" + type = "project" + permissions = flatten([for role in data.spectrocloud_role.roles : role.permissions]) +} +``` + + +{{ .SchemaMarkdown | trimspace }} \ No newline at end of file