Skip to content

Commit

Permalink
Added space roles datasource
Browse files Browse the repository at this point in the history
  • Loading branch information
Dray56 authored and debTheRay committed Oct 23, 2024
1 parent 47c3e20 commit b119f06
Show file tree
Hide file tree
Showing 10 changed files with 1,245 additions and 0 deletions.
51 changes: 51 additions & 0 deletions docs/data-sources/space_roles.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
page_title: "cloudfoundry_space_roles Data Source - terraform-provider-cloudfoundry"
subcategory: ""
description: |-
Gets information on Cloud Foundry roles within a space.
---

# cloudfoundry_space_roles (Data Source)

Gets information on Cloud Foundry roles within a space.

## Example Usage

```terraform
data "cloudfoundry_space_roles" "roles" {
space = "02c0cc92-6ecc-44b1-b7b2-096ca19ee143"
type = "space_developer"
}
output "role_objects" {
value = data.cloudfoundry_space_roles.roles
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `space` (String) The guid of the space the role is assigned to

### Optional

- `type` (String) Valid space role type to filter for; see [Valid role types](https://v3-apidocs.cloudfoundry.org/version/3.154.0/index.html#valid-role-types)
- `user` (String) The guid of the cloudfoundry user the role is assigned to for filtering

### Read-Only

- `roles` (Attributes List) The list of space roles (see [below for nested schema](#nestedatt--roles))

<a id="nestedatt--roles"></a>
### Nested Schema for `roles`

Read-Only:

- `created_at` (String) The date and time when the resource was created in [RFC3339](https://www.ietf.org/rfc/rfc3339.txt) format.
- `id` (String) The guid for the role
- `space` (String) The guid of the organization the role is assigned to
- `type` (String) role type
- `updated_at` (String) The date and time when the resource was updated in [RFC3339](https://www.ietf.org/rfc/rfc3339.txt) format.
- `user` (String) The guid of the cloudfoundry user the role is assigned to
8 changes: 8 additions & 0 deletions examples/data-sources/cloudfoundry_space_roles/data-source.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
data "cloudfoundry_space_roles" "roles" {
space = "02c0cc92-6ecc-44b1-b7b2-096ca19ee143"
type = "space_developer"
}

output "role_objects" {
value = data.cloudfoundry_space_roles.roles
}
170 changes: 170 additions & 0 deletions internal/provider/datasource_space_roles.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package provider

import (
"context"
"fmt"

"github.com/cloudfoundry/go-cfclient/v3/client"
"github.com/cloudfoundry/terraform-provider-cloudfoundry/internal/provider/managers"
"github.com/cloudfoundry/terraform-provider-cloudfoundry/internal/validation"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-log/tflog"
)

// Ensure provider defined types fully satisfy framework interfaces.
var (
_ datasource.DataSource = &SpaceRolesDataSource{}
_ datasource.DataSourceWithConfigure = &SpaceRolesDataSource{}
)

// Instantiates a space role data source.
func NewSpaceRolesDataSource() datasource.DataSource {
return &SpaceRolesDataSource{}
}

// Contains reference to the v3 client to be used for making the API calls.
type SpaceRolesDataSource struct {
cfClient *client.Client
}

func (d *SpaceRolesDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_space_roles"
}

func (d *SpaceRolesDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
// Prevent panic if the provider has not been configured.
if req.ProviderData == nil {
return
}
session, ok := req.ProviderData.(*managers.Session)
if !ok {
resp.Diagnostics.AddError(
"Unexpected Data Source Configure Type",
fmt.Sprintf("Expected *managers.Session, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)
return
}
d.cfClient = session.CFClient
}

func (d *SpaceRolesDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
MarkdownDescription: "Gets information on Cloud Foundry roles within a space.",
Attributes: map[string]schema.Attribute{
"space": schema.StringAttribute{
MarkdownDescription: "The guid of the space the role is assigned to",
Required: true,
Validators: []validator.String{
validation.ValidUUID(),
},
},
"type": schema.StringAttribute{
MarkdownDescription: "Valid space role type to filter for; see [Valid role types](https://v3-apidocs.cloudfoundry.org/version/3.154.0/index.html#valid-role-types)",
Optional: true,
Validators: []validator.String{
stringvalidator.OneOf("space_auditor", "space_developer", "space_manager", "space_supporter"),
},
},
"user": schema.StringAttribute{
MarkdownDescription: "The guid of the cloudfoundry user the role is assigned to for filtering",
Optional: true,
Validators: []validator.String{
validation.ValidUUID(),
},
},
"roles": schema.ListNestedAttribute{
MarkdownDescription: "The list of space roles",
Computed: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
MarkdownDescription: "The guid for the role",
Computed: true,
},
"type": schema.StringAttribute{
MarkdownDescription: "role type",
Computed: true,
},
"user": schema.StringAttribute{
MarkdownDescription: "The guid of the cloudfoundry user the role is assigned to",
Computed: true,
},
"space": schema.StringAttribute{
MarkdownDescription: "The guid of the organization the role is assigned to",
Computed: true,
},
createdAtKey: createdAtSchema(),
updatedAtKey: updatedAtSchema(),
},
},
},
},
}
}

func (d *SpaceRolesDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data spaceRolesDatasourceType
diags := req.Config.Get(ctx, &data)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

_, err := d.cfClient.Spaces.Get(ctx, data.Space.ValueString())
if err != nil {
resp.Diagnostics.AddError(
"API Error Fetching Space",
"Could not get space with ID "+data.Space.ValueString()+" : "+err.Error(),
)
return
}

spaceRolesListOptions := client.RoleListOptions{
SpaceGUIDs: client.Filter{
Values: []string{
data.Space.ValueString(),
},
},
}

if !data.Type.IsNull() {
spaceRolesListOptions.Types = client.Filter{
Values: []string{
data.Type.ValueString(),
},
}
}
if !data.User.IsNull() {
spaceRolesListOptions.UserGUIDs = client.Filter{
Values: []string{
data.User.ValueString(),
},
}
}

roles, err := d.cfClient.Roles.ListAll(ctx, &spaceRolesListOptions)
if err != nil {
resp.Diagnostics.AddError(
"Unable to fetch space roles data.",
fmt.Sprintf("Request failed with %s.", err.Error()),
)
return
}

if len(roles) == 0 {
resp.Diagnostics.AddError(
"Unable to find any roles in list",
fmt.Sprintf("No roles present under space %s with mentioned criteria", data.Space.ValueString()),
)
return
}

data.Roles = mapSpaceRolesValuesToType(roles)

tflog.Trace(ctx, "read a space roles data source")
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)

}
118 changes: 118 additions & 0 deletions internal/provider/datasource_space_roles_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package provider

import (
"bytes"
"regexp"
"testing"
"text/template"

"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)

type SpaceRolesModelPtr struct {
HclType string
HclObjectName string
Type *string
User *string
Space *string
Roles *string
}

func hclSpaceRolesDataSource(rrmp *SpaceRolesModelPtr) string {
if rrmp != nil {
s := `
{{.HclType}} "cloudfoundry_space_roles" {{.HclObjectName}} {
{{- if .Type}}
type = "{{.Type}}"
{{- end -}}
{{if .User}}
user = "{{.User}}"
{{- end -}}
{{if .Space}}
space = "{{.Space}}"
{{- end -}}
{{if .Roles}}
roles = "{{.Roles}}"
{{- end }}
}`
tmpl, err := template.New("datasource_role").Parse(s)
if err != nil {
panic(err)
}
buf := new(bytes.Buffer)
err = tmpl.Execute(buf, rrmp)
if err != nil {
panic(err)
}
return buf.String()
}
return rrmp.HclType + ` "cloudfoundry_space_roles" ` + rrmp.HclObjectName + ` {}`
}

func TestSpaceRolesDataSource_Configure(t *testing.T) {
testSpaceGuid := "02c0cc92-6ecc-44b1-b7b2-096ca19ee143"
t.Parallel()
dataSourceName := "data.cloudfoundry_space_roles.ds"
t.Run("happy path - read space role", func(t *testing.T) {
cfg := getCFHomeConf()
rec := cfg.SetupVCR(t, "fixtures/datasource_space_roles")
defer stopQuietly(rec)

resource.Test(t, resource.TestCase{
IsUnitTest: true,
ProtoV6ProviderFactories: getProviders(rec.GetDefaultClient()),
Steps: []resource.TestStep{
{
Config: hclProvider(nil) + hclSpaceRolesDataSource(&SpaceRolesModelPtr{
HclType: hclObjectDataSource,
HclObjectName: "ds",
Space: strtostrptr(testSpaceGuid),
}),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(dataSourceName, "space", testSpaceGuid),
resource.TestCheckResourceAttr(dataSourceName, "roles.#", "8")),
},
{
Config: hclProvider(nil) + hclSpaceRolesDataSource(&SpaceRolesModelPtr{
HclType: hclObjectDataSource,
HclObjectName: "ds",
Space: strtostrptr(testSpaceGuid),
Type: strtostrptr("space_developer"),
}),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(dataSourceName, "space", testSpaceGuid),
resource.TestCheckResourceAttr(dataSourceName, "roles.#", "5")),
},
},
})
})
t.Run("error path - role does not exist", func(t *testing.T) {
cfg := getCFHomeConf()
rec := cfg.SetupVCR(t, "fixtures/datasource_space_roles_invalid")
defer stopQuietly(rec)

resource.Test(t, resource.TestCase{
IsUnitTest: true,
ProtoV6ProviderFactories: getProviders(rec.GetDefaultClient()),
Steps: []resource.TestStep{
{
Config: hclProvider(nil) + hclSpaceRolesDataSource(&SpaceRolesModelPtr{
HclType: hclObjectDataSource,
HclObjectName: "ds",
Type: strtostrptr("space_developers"),
Space: strtostrptr(invalidOrgGUID),
}),
ExpectError: regexp.MustCompile(`Invalid Attribute Value Match`),
},
{
Config: hclProvider(nil) + hclSpaceRolesDataSource(&SpaceRolesModelPtr{
HclType: hclObjectDataSource,
HclObjectName: "ds",
Space: strtostrptr(invalidOrgGUID),
}),
ExpectError: regexp.MustCompile(`API Error Fetching Space`),
},
},
})
})
}
Loading

0 comments on commit b119f06

Please sign in to comment.