Skip to content

Commit bf19e23

Browse files
committed
Add CloudStack project resource
1 parent a653531 commit bf19e23

File tree

6 files changed

+512
-0
lines changed

6 files changed

+512
-0
lines changed

cloudstack/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ func Provider() *schema.Provider {
111111
"cloudstack_nic": resourceCloudStackNIC(),
112112
"cloudstack_port_forward": resourceCloudStackPortForward(),
113113
"cloudstack_private_gateway": resourceCloudStackPrivateGateway(),
114+
"cloudstack_project": resourceCloudStackProject(),
114115
"cloudstack_secondary_ipaddress": resourceCloudStackSecondaryIPAddress(),
115116
"cloudstack_security_group": resourceCloudStackSecurityGroup(),
116117
"cloudstack_security_group_rule": resourceCloudStackSecurityGroupRule(),
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
//
18+
19+
package cloudstack
20+
21+
import (
22+
"fmt"
23+
"log"
24+
"strings"
25+
26+
"github.com/apache/cloudstack-go/v2/cloudstack"
27+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
28+
)
29+
30+
func resourceCloudStackProject() *schema.Resource {
31+
return &schema.Resource{
32+
Create: resourceCloudStackProjectCreate,
33+
Read: resourceCloudStackProjectRead,
34+
Update: resourceCloudStackProjectUpdate,
35+
Delete: resourceCloudStackProjectDelete,
36+
Importer: &schema.ResourceImporter{
37+
State: importStatePassthrough,
38+
},
39+
40+
Schema: map[string]*schema.Schema{
41+
"name": {
42+
Type: schema.TypeString,
43+
Required: true,
44+
},
45+
46+
"display_text": {
47+
Type: schema.TypeString,
48+
Optional: true,
49+
Computed: true,
50+
},
51+
52+
"domain": {
53+
Type: schema.TypeString,
54+
Optional: true,
55+
Computed: true,
56+
ForceNew: true,
57+
},
58+
59+
"account": {
60+
Type: schema.TypeString,
61+
Optional: true,
62+
ForceNew: true,
63+
},
64+
65+
"accountid": {
66+
Type: schema.TypeString,
67+
Optional: true,
68+
ForceNew: true,
69+
},
70+
71+
"userid": {
72+
Type: schema.TypeString,
73+
Optional: true,
74+
ForceNew: true,
75+
},
76+
},
77+
}
78+
}
79+
80+
func resourceCloudStackProjectCreate(d *schema.ResourceData, meta interface{}) error {
81+
cs := meta.(*cloudstack.CloudStackClient)
82+
83+
// Get the name and display_text
84+
name := d.Get("name").(string)
85+
displaytext := name
86+
if v, ok := d.GetOk("display_text"); ok {
87+
displaytext = v.(string)
88+
}
89+
90+
// The CloudStack API expects displaytext as the first parameter and name as the second
91+
p := cs.Project.NewCreateProjectParams(name, displaytext)
92+
93+
// Set the domain if provided
94+
if domain, ok := d.GetOk("domain"); ok {
95+
domainid, e := retrieveID(cs, "domain", domain.(string))
96+
if e != nil {
97+
return e.Error()
98+
}
99+
p.SetDomainid(domainid)
100+
}
101+
102+
// Set the account if provided
103+
if account, ok := d.GetOk("account"); ok {
104+
p.SetAccount(account.(string))
105+
}
106+
107+
// Set the accountid if provided
108+
if accountid, ok := d.GetOk("accountid"); ok {
109+
p.SetAccountid(accountid.(string))
110+
}
111+
112+
// Set the userid if provided
113+
if userid, ok := d.GetOk("userid"); ok {
114+
p.SetUserid(userid.(string))
115+
}
116+
117+
log.Printf("[DEBUG] Creating project %s", name)
118+
r, err := cs.Project.CreateProject(p)
119+
if err != nil {
120+
return fmt.Errorf("Error creating project %s: %s", name, err)
121+
}
122+
123+
d.SetId(r.Id)
124+
125+
return resourceCloudStackProjectRead(d, meta)
126+
}
127+
128+
func resourceCloudStackProjectRead(d *schema.ResourceData, meta interface{}) error {
129+
cs := meta.(*cloudstack.CloudStackClient)
130+
131+
log.Printf("[DEBUG] Retrieving project %s", d.Id())
132+
133+
// Get the project details
134+
p := cs.Project.NewListProjectsParams()
135+
p.SetId(d.Id())
136+
137+
l, err := cs.Project.ListProjects(p)
138+
if err != nil {
139+
if strings.Contains(err.Error(), fmt.Sprintf(
140+
"Invalid parameter id value=%s due to incorrect long value format, "+
141+
"or entity does not exist", d.Id())) {
142+
log.Printf("[DEBUG] Project %s does no longer exist", d.Id())
143+
d.SetId("")
144+
return nil
145+
}
146+
147+
return err
148+
}
149+
150+
if l.Count == 0 {
151+
log.Printf("[DEBUG] Project %s does no longer exist", d.Id())
152+
d.SetId("")
153+
return nil
154+
}
155+
156+
project := l.Projects[0]
157+
// The CloudStack API seems to swap name and display_text, so we need to swap them back
158+
d.Set("name", project.Displaytext)
159+
d.Set("display_text", project.Name)
160+
d.Set("domain", project.Domain)
161+
162+
// Only set the account, accountid, and userid if they were explicitly set in the configuration
163+
if _, ok := d.GetOk("account"); ok && len(project.Owner) > 0 {
164+
for _, owner := range project.Owner {
165+
if account, ok := owner["account"]; ok {
166+
d.Set("account", account)
167+
}
168+
}
169+
}
170+
171+
if _, ok := d.GetOk("accountid"); ok && len(project.Owner) > 0 {
172+
for _, owner := range project.Owner {
173+
if accountid, ok := owner["accountid"]; ok {
174+
d.Set("accountid", accountid)
175+
}
176+
}
177+
}
178+
179+
if _, ok := d.GetOk("userid"); ok && len(project.Owner) > 0 {
180+
for _, owner := range project.Owner {
181+
if userid, ok := owner["userid"]; ok {
182+
d.Set("userid", userid)
183+
}
184+
}
185+
}
186+
187+
return nil
188+
}
189+
190+
func resourceCloudStackProjectUpdate(d *schema.ResourceData, meta interface{}) error {
191+
cs := meta.(*cloudstack.CloudStackClient)
192+
193+
// Check if the name or display text is changed
194+
if d.HasChange("name") || d.HasChange("display_text") {
195+
// Create a new parameter struct
196+
p := cs.Project.NewUpdateProjectParams(d.Id())
197+
198+
// The CloudStack API seems to swap name and display_text, so we need to swap them here
199+
if d.HasChange("name") {
200+
p.SetDisplaytext(d.Get("name").(string))
201+
}
202+
203+
if d.HasChange("display_text") {
204+
p.SetName(d.Get("display_text").(string))
205+
}
206+
207+
log.Printf("[DEBUG] Updating project %s", d.Id())
208+
_, err := cs.Project.UpdateProject(p)
209+
if err != nil {
210+
return fmt.Errorf("Error updating project %s: %s", d.Id(), err)
211+
}
212+
}
213+
214+
return resourceCloudStackProjectRead(d, meta)
215+
}
216+
217+
func resourceCloudStackProjectDelete(d *schema.ResourceData, meta interface{}) error {
218+
cs := meta.(*cloudstack.CloudStackClient)
219+
220+
// Create a new parameter struct
221+
p := cs.Project.NewDeleteProjectParams(d.Id())
222+
223+
log.Printf("[INFO] Deleting project: %s", d.Id())
224+
_, err := cs.Project.DeleteProject(p)
225+
if err != nil {
226+
// This is a very poor way to be told the ID does no longer exist :(
227+
if strings.Contains(err.Error(), fmt.Sprintf(
228+
"Invalid parameter id value=%s due to incorrect long value format, "+
229+
"or entity does not exist", d.Id())) {
230+
return nil
231+
}
232+
233+
return fmt.Errorf("Error deleting project %s: %s", d.Id(), err)
234+
}
235+
236+
return nil
237+
}

0 commit comments

Comments
 (0)