Skip to content

Commit b4b9f6d

Browse files
authored
Merge pull request #181 from ianc769/feature/role
Add `cloudstack_role` resource
2 parents 1216148 + 829eb42 commit b4b9f6d

8 files changed

+636
-0
lines changed
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
//
2+
// Licensed to the Apache Software Foundation (ASF) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The ASF licenses this file
6+
// to you under the Apache License, Version 2.0 (the
7+
// "License"); you may not use this file except in compliance
8+
// with the License. You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
//
19+
20+
package cloudstack
21+
22+
import (
23+
"encoding/json"
24+
"fmt"
25+
"log"
26+
"regexp"
27+
"strings"
28+
29+
"github.com/apache/cloudstack-go/v2/cloudstack"
30+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
31+
)
32+
33+
func dataSourceCloudstackRole() *schema.Resource {
34+
return &schema.Resource{
35+
Read: dataSourceCloudstackRoleRead,
36+
Schema: map[string]*schema.Schema{
37+
"filter": dataSourceFiltersSchema(),
38+
39+
//Computed values
40+
"id": {
41+
Type: schema.TypeString,
42+
Computed: true,
43+
},
44+
45+
"name": {
46+
Type: schema.TypeString,
47+
Computed: true,
48+
},
49+
50+
"type": {
51+
Type: schema.TypeString,
52+
Computed: true,
53+
},
54+
55+
"description": {
56+
Type: schema.TypeString,
57+
Computed: true,
58+
},
59+
60+
"is_public": {
61+
Type: schema.TypeBool,
62+
Computed: true,
63+
},
64+
},
65+
}
66+
}
67+
68+
func dataSourceCloudstackRoleRead(d *schema.ResourceData, meta interface{}) error {
69+
cs := meta.(*cloudstack.CloudStackClient)
70+
p := cs.Role.NewListRolesParams()
71+
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")
78+
var role *cloudstack.Role
79+
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+
}
89+
}
90+
91+
if role == nil {
92+
return fmt.Errorf("no role is matching with the specified criteria")
93+
}
94+
log.Printf("[DEBUG] Selected role: %s\n", role.Name)
95+
96+
return roleDescriptionAttributes(d, role)
97+
}
98+
99+
func roleDescriptionAttributes(d *schema.ResourceData, role *cloudstack.Role) error {
100+
d.SetId(role.Id)
101+
d.Set("name", role.Name)
102+
d.Set("type", role.Type)
103+
d.Set("description", role.Description)
104+
d.Set("is_public", role.Ispublic)
105+
106+
return nil
107+
}
108+
109+
func applyRoleFilters(role *cloudstack.Role, filters *schema.Set) (bool, error) {
110+
var roleJSON map[string]interface{}
111+
k, _ := json.Marshal(role)
112+
err := json.Unmarshal(k, &roleJSON)
113+
if err != nil {
114+
return false, err
115+
}
116+
117+
for _, f := range filters.List() {
118+
m := f.(map[string]interface{})
119+
r, err := regexp.Compile(m["value"].(string))
120+
if err != nil {
121+
return false, fmt.Errorf("invalid regex: %s", err)
122+
}
123+
updatedName := strings.ReplaceAll(m["name"].(string), "_", "")
124+
125+
// Check if the field exists in the role JSON
126+
roleField, ok := roleJSON[updatedName]
127+
if !ok {
128+
return false, fmt.Errorf("field %s does not exist in role", updatedName)
129+
}
130+
131+
// Convert the field to string for regex matching
132+
var roleFieldStr string
133+
switch v := roleField.(type) {
134+
case string:
135+
roleFieldStr = v
136+
case bool:
137+
roleFieldStr = fmt.Sprintf("%t", v)
138+
case float64:
139+
roleFieldStr = fmt.Sprintf("%g", v)
140+
default:
141+
roleFieldStr = fmt.Sprintf("%v", v)
142+
}
143+
144+
if !r.MatchString(roleFieldStr) {
145+
return false, nil
146+
}
147+
}
148+
149+
return true, nil
150+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//
2+
// Licensed to the Apache Software Foundation (ASF) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The ASF licenses this file
6+
// to you under the Apache License, Version 2.0 (the
7+
// "License"); you may not use this file except in compliance
8+
// with the License. You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
//
19+
20+
package cloudstack
21+
22+
import (
23+
"testing"
24+
25+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
26+
)
27+
28+
func TestAccDataSourceCloudStackRole_basic(t *testing.T) {
29+
resource.Test(t, resource.TestCase{
30+
PreCheck: func() { testAccPreCheck(t) },
31+
Providers: testAccProviders,
32+
Steps: []resource.TestStep{
33+
{
34+
Config: testAccDataSourceCloudStackRole_basic,
35+
Check: resource.ComposeTestCheckFunc(
36+
resource.TestCheckResourceAttr(
37+
"data.cloudstack_role.role", "name", "terraform-role"),
38+
resource.TestCheckResourceAttr(
39+
"data.cloudstack_role.role", "description", "terraform test role"),
40+
resource.TestCheckResourceAttr(
41+
"data.cloudstack_role.role", "is_public", "true"),
42+
),
43+
},
44+
},
45+
})
46+
}
47+
48+
const testAccDataSourceCloudStackRole_basic = `
49+
resource "cloudstack_role" "foo" {
50+
name = "terraform-role"
51+
description = "terraform test role"
52+
is_public = true
53+
type = "User"
54+
}
55+
56+
data "cloudstack_role" "role" {
57+
filter {
58+
name = "name"
59+
value = "${cloudstack_role.foo.name}"
60+
}
61+
}
62+
`

cloudstack/provider.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ func Provider() *schema.Provider {
9292
"cloudstack_pod": dataSourceCloudstackPod(),
9393
"cloudstack_domain": dataSourceCloudstackDomain(),
9494
"cloudstack_physicalnetwork": dataSourceCloudStackPhysicalNetwork(),
95+
"cloudstack_role": dataSourceCloudstackRole(),
9596
},
9697

9798
ResourcesMap: map[string]*schema.Resource{
@@ -136,6 +137,7 @@ func Provider() *schema.Provider {
136137
"cloudstack_physicalnetwork": resourceCloudStackPhysicalNetwork(),
137138
"cloudstack_traffic_type": resourceCloudStackTrafficType(),
138139
"cloudstack_network_service_provider": resourceCloudStackNetworkServiceProvider(),
140+
"cloudstack_role": resourceCloudStackRole(),
139141
},
140142

141143
ConfigureFunc: providerConfigure,

0 commit comments

Comments
 (0)