Skip to content

Commit 55ff8c1

Browse files
committed
Enhance cloudstack_role data source and resource with filter support and improved documentation
- Added filter support to the cloudstack_role data source for role retrieval. - Updated resource_cloudstack_role to require either role_id or type. - Enhanced documentation for both data source and resource with examples and argument descriptions.
1 parent be71510 commit 55ff8c1

File tree

5 files changed

+134
-27
lines changed

5 files changed

+134
-27
lines changed

cloudstack/data_source_cloudstack_role.go

Lines changed: 79 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,11 @@
2020
package cloudstack
2121

2222
import (
23+
"encoding/json"
2324
"fmt"
2425
"log"
26+
"regexp"
27+
"strings"
2528

2629
"github.com/apache/cloudstack-go/v2/cloudstack"
2730
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
@@ -33,15 +36,14 @@ func dataSourceCloudstackRole() *schema.Resource {
3336
Schema: map[string]*schema.Schema{
3437
"filter": dataSourceFiltersSchema(),
3538

39+
//Computed values
3640
"id": {
3741
Type: schema.TypeString,
38-
Optional: true,
3942
Computed: true,
4043
},
4144

4245
"name": {
4346
Type: schema.TypeString,
44-
Optional: true,
4547
Computed: true,
4648
},
4749

@@ -65,24 +67,36 @@ func dataSourceCloudstackRole() *schema.Resource {
6567

6668
func dataSourceCloudstackRoleRead(d *schema.ResourceData, meta interface{}) error {
6769
cs := meta.(*cloudstack.CloudStackClient)
70+
p := cs.Role.NewListRolesParams()
6871

69-
var err error
72+
csRoles, err := cs.Role.ListRoles(p)
73+
if err != nil {
74+
return fmt.Errorf("failed to list roles: %s", err)
75+
}
76+
77+
filters := d.Get("filter")
7078
var role *cloudstack.Role
7179

72-
if id, ok := d.GetOk("id"); ok {
73-
log.Printf("[DEBUG] Getting Role by ID: %s", id.(string))
74-
role, _, err = cs.Role.GetRoleByID(id.(string))
75-
} else if name, ok := d.GetOk("name"); ok {
76-
log.Printf("[DEBUG] Getting Role by name: %s", name.(string))
77-
role, _, err = cs.Role.GetRoleByName(name.(string))
78-
} else {
79-
return fmt.Errorf("Either 'id' or 'name' must be specified")
80+
for _, r := range csRoles.Roles {
81+
match, err := applyRoleFilters(r, filters.(*schema.Set))
82+
if err != nil {
83+
return err
84+
}
85+
if match {
86+
role = r
87+
break
88+
}
8089
}
8190

82-
if err != nil {
83-
return err
91+
if role == nil {
92+
return fmt.Errorf("no role is matching with the specified criteria")
8493
}
94+
log.Printf("[DEBUG] Selected role: %s\n", role.Name)
95+
96+
return roleDescriptionAttributes(d, role)
97+
}
8598

99+
func roleDescriptionAttributes(d *schema.ResourceData, role *cloudstack.Role) error {
86100
d.SetId(role.Id)
87101
d.Set("name", role.Name)
88102
d.Set("type", role.Type)
@@ -91,3 +105,55 @@ func dataSourceCloudstackRoleRead(d *schema.ResourceData, meta interface{}) erro
91105

92106
return nil
93107
}
108+
109+
func latestRole(roles []*cloudstack.Role) (*cloudstack.Role, error) {
110+
// Since the Role struct doesn't have a Created field,
111+
// we'll just return the first role in the list
112+
if len(roles) > 0 {
113+
return roles[0], nil
114+
}
115+
return nil, fmt.Errorf("no roles found")
116+
}
117+
118+
func applyRoleFilters(role *cloudstack.Role, filters *schema.Set) (bool, error) {
119+
var roleJSON map[string]interface{}
120+
k, _ := json.Marshal(role)
121+
err := json.Unmarshal(k, &roleJSON)
122+
if err != nil {
123+
return false, err
124+
}
125+
126+
for _, f := range filters.List() {
127+
m := f.(map[string]interface{})
128+
r, err := regexp.Compile(m["value"].(string))
129+
if err != nil {
130+
return false, fmt.Errorf("invalid regex: %s", err)
131+
}
132+
updatedName := strings.ReplaceAll(m["name"].(string), "_", "")
133+
134+
// Check if the field exists in the role JSON
135+
roleField, ok := roleJSON[updatedName]
136+
if !ok {
137+
return false, fmt.Errorf("field %s does not exist in role", updatedName)
138+
}
139+
140+
// Convert the field to string for regex matching
141+
var roleFieldStr string
142+
switch v := roleField.(type) {
143+
case string:
144+
roleFieldStr = v
145+
case bool:
146+
roleFieldStr = fmt.Sprintf("%t", v)
147+
case float64:
148+
roleFieldStr = fmt.Sprintf("%g", v)
149+
default:
150+
roleFieldStr = fmt.Sprintf("%v", v)
151+
}
152+
153+
if !r.MatchString(roleFieldStr) {
154+
return false, nil
155+
}
156+
}
157+
158+
return true, nil
159+
}

cloudstack/data_source_cloudstack_role_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ resource "cloudstack_role" "foo" {
5353
}
5454
5555
data "cloudstack_role" "role" {
56-
name = "${cloudstack_role.foo.name}"
56+
filter {
57+
name = "name"
58+
value = "${cloudstack_role.foo.name}"
59+
}
5760
}
5861
`

cloudstack/resource_cloudstack_role.go

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,19 +39,27 @@ func resourceCloudStackRole() *schema.Resource {
3939
Required: true,
4040
},
4141
"type": {
42-
Type: schema.TypeString,
43-
Optional: true,
44-
Computed: true,
42+
Type: schema.TypeString,
43+
Optional: true,
44+
Computed: true,
45+
Description: "The type of the role, valid options are: Admin, ResourceAdmin, DomainAdmin, User",
4546
},
4647
"description": {
4748
Type: schema.TypeString,
4849
Optional: true,
4950
Computed: true,
5051
},
5152
"is_public": {
52-
Type: schema.TypeBool,
53-
Optional: true,
54-
Default: false,
53+
Type: schema.TypeBool,
54+
Optional: true,
55+
Default: true,
56+
Description: "Indicates whether the role will be visible to all users (public) or only to root admins (private). Default is true.",
57+
},
58+
"role_id": {
59+
Type: schema.TypeString,
60+
Optional: true,
61+
ForceNew: true,
62+
Description: "ID of the role to be cloned from. Either role_id or type must be passed in.",
5563
},
5664
},
5765
}
@@ -64,8 +72,17 @@ func resourceCloudStackRoleCreate(d *schema.ResourceData, meta interface{}) erro
6472
// Create a new parameter struct
6573
p := cs.Role.NewCreateRoleParams(name)
6674

67-
if roleType, ok := d.GetOk("type"); ok {
75+
// Check if either role_id or type is provided
76+
roleID, roleIDOk := d.GetOk("role_id")
77+
roleType, roleTypeOk := d.GetOk("type")
78+
79+
if roleIDOk {
80+
p.SetRoleid(roleID.(string))
81+
} else if roleTypeOk {
6882
p.SetType(roleType.(string))
83+
} else {
84+
// According to the API, either roleid or type must be passed in
85+
return fmt.Errorf("either role_id or type must be specified")
6986
}
7087

7188
if description, ok := d.GetOk("description"); ok {

website/docs/d/role.html.markdown

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ Use this data source to get information about a role for use in other resources.
1414

1515
```hcl
1616
data "cloudstack_role" "admin" {
17-
name = "Admin"
17+
filter {
18+
name = "name"
19+
value = "Admin"
20+
}
1821
}
1922
2023
resource "cloudstack_account" "example" {
@@ -32,10 +35,18 @@ resource "cloudstack_account" "example" {
3235

3336
The following arguments are supported:
3437

35-
* `id` - (Optional) The ID of the role.
36-
* `name` - (Optional) The name of the role.
38+
* `filter` - (Required) One or more name/value pairs to filter off of. See the example below for usage.
3739

38-
At least one of the above arguments is required.
40+
## Filter Example
41+
42+
```hcl
43+
data "cloudstack_role" "admin" {
44+
filter {
45+
name = "name"
46+
value = "Admin"
47+
}
48+
}
49+
```
3950

4051
## Attributes Reference
4152

website/docs/r/role.html.markdown

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,32 @@ Creates a role.
1313
## Example Usage
1414

1515
```hcl
16+
# Create a role with a specific type
1617
resource "cloudstack_role" "admin" {
1718
name = "Admin"
1819
type = "Admin"
1920
description = "Administrator role"
2021
is_public = true
2122
}
23+
24+
# Create a role by cloning an existing role
25+
resource "cloudstack_role" "custom_admin" {
26+
name = "CustomAdmin"
27+
role_id = "12345678-1234-1234-1234-123456789012"
28+
description = "Custom administrator role cloned from an existing role"
29+
is_public = false
30+
}
2231
```
2332

2433
## Argument Reference
2534

2635
The following arguments are supported:
2736

2837
* `name` - (Required) The name of the role.
29-
* `type` - (Optional) The type of the role. Defaults to the CloudStack default.
38+
* `type` - (Optional) The type of the role. Valid options are: Admin, ResourceAdmin, DomainAdmin, User. Either `type` or `role_id` must be specified.
3039
* `description` - (Optional) The description of the role.
31-
* `is_public` - (Optional) Whether the role is public or not. Defaults to `false`.
40+
* `is_public` - (Optional) Whether the role is public or not. Defaults to `true`.
41+
* `role_id` - (Optional) ID of the role to be cloned from. Either `role_id` or `type` must be specified.
3242

3343
## Attributes Reference
3444

0 commit comments

Comments
 (0)