Skip to content

Commit c5356b1

Browse files
committed
Merge branch 'main' into feature/project
2 parents 1296923 + 762f63f commit c5356b1

File tree

8 files changed

+409
-44
lines changed

8 files changed

+409
-44
lines changed

.github/workflows/setup-cloudstack/action.yml renamed to .github/actions/setup-cloudstack/action.yml

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -49,33 +49,57 @@ runs:
4949
((T+=1))
5050
sleep 30
5151
done
52+
53+
# After loop, check if Cloudstack is up
54+
if ! curl -sfSL http://localhost:8080 --output /dev/null; then
55+
echo "Cloudstack did not become ready in time"
56+
curl -v http://localhost:8080 || true
57+
exit 22
58+
fi
5259
- name: Setting up Cloudstack
5360
id: setup-cloudstack
5461
shell: bash
5562
run: |
56-
docker exec $(docker container ls --format=json -l | jq -r .ID) python /root/tools/marvin/marvin/deployDataCenter.py -i /root/setup/dev/advanced.cfg
57-
curl -sf --location "${CLOUDSTACK_API_URL}" \
58-
--header 'Content-Type: application/x-www-form-urlencoded' \
59-
--data-urlencode 'command=login' \
60-
--data-urlencode 'username=admin' \
61-
--data-urlencode 'password=password' \
62-
--data-urlencode 'response=json' \
63-
--data-urlencode 'domain=/' -j -c cookies.txt --output /dev/null
64-
65-
CLOUDSTACK_USER_ID=$(curl -fs "${CLOUDSTACK_API_URL}?command=listUsers&response=json" -b cookies.txt | jq -r '.listusersresponse.user[0].id')
66-
CLOUDSTACK_API_KEY=$(curl -s "${CLOUDSTACK_API_URL}?command=getUserKeys&id=${CLOUDSTACK_USER_ID}&response=json" -b cookies.txt | jq -r '.getuserkeysresponse.userkeys.apikey')
67-
CLOUDSTACK_SECRET_KEY=$(curl -fs "${CLOUDSTACK_API_URL}?command=getUserKeys&id=${CLOUDSTACK_USER_ID}&response=json" -b cookies.txt | jq -r '.getuserkeysresponse.userkeys.secretkey')
63+
64+
set -euo pipefail
65+
66+
echo "Deploying Data Center..."
67+
docker exec $(docker container ls --format=json -l | jq -r .ID) \
68+
python /root/tools/marvin/marvin/deployDataCenter.py -i /root/setup/dev/advanced.cfg
69+
70+
# Get the container ID of the running simulator
71+
CONTAINER_ID=$(docker ps --filter "ancestor=apache/cloudstack-simulator:${{ matrix.cloudstack-version }}" --format "{{.ID}}" | head -n1)
72+
73+
# Install CloudMonkey
74+
docker exec $CONTAINER_ID bash -c "curl -sfSL https://github.com/apache/cloudstack-cloudmonkey/releases/download/6.4.0/cmk.linux.x86-64 -o /usr/local/bin/cmk && chmod +x /usr/local/bin/cmk"
75+
76+
# Write the CloudMonkey config file with a profile using user/pass
77+
docker exec $CONTAINER_ID mkdir -p /root/.cmk
78+
docker exec $CONTAINER_ID bash -c "printf '[core]\nprofile = localcloud\n\n[localcloud]\nurl = http://localhost:8080/client/api\nusername = admin\npassword = password\ndomain = /\napikey =\nsecretkey =\ntimeout = 3600\n' > /root/.cmk/config"
79+
80+
81+
# Use CloudMonkey with the profile to list users and extract API key/secret key
82+
docker exec $CONTAINER_ID cmk -p localcloud list users --output json > users.json
83+
84+
# Generate API keys
85+
docker exec $CONTAINER_ID cmk -p localcloud registeruserKeys id=$(jq -r '.user[0].id' users.json) > api.json
86+
87+
# Extract the first user's keys (assuming admin is first)
88+
CLOUDSTACK_API_KEY=$(jq -r '.userkeys.apikey' api.json)
89+
CLOUDSTACK_SECRET_KEY=$(jq -r '.userkeys.secretkey' api.json)
90+
CLOUDSTACK_USER_ID=$(jq -r '.user[0].id' users.json)
6891
6992
echo "::add-mask::$CLOUDSTACK_API_KEY"
7093
echo "::add-mask::$CLOUDSTACK_SECRET_KEY"
7194
7295
echo "user_id=$CLOUDSTACK_USER_ID" >> $GITHUB_OUTPUT
7396
echo "api_key=$CLOUDSTACK_API_KEY" >> $GITHUB_OUTPUT
7497
echo "secret_key=$CLOUDSTACK_SECRET_KEY" >> $GITHUB_OUTPUT
98+
7599
- name: Install CMK
76100
shell: bash
77101
run: |
78-
curl -sfL https://github.com/apache/cloudstack-cloudmonkey/releases/download/6.3.0/cmk.linux.x86-64 -o /usr/local/bin/cmk
102+
curl -sfSL https://github.com/apache/cloudstack-cloudmonkey/releases/download/6.4.0/cmk.linux.x86-64 -o /usr/local/bin/cmk
79103
chmod +x /usr/local/bin/cmk
80104
- name: Create extra resources
81105
shell: bash

.github/workflows/acceptance.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ permissions:
3030

3131
env:
3232
CLOUDSTACK_API_URL: http://localhost:8080/client/api
33-
CLOUDSTACK_VERSIONS: "['4.18.2.1', '4.19.0.2']"
33+
CLOUDSTACK_VERSIONS: "['4.19.0.1', '4.19.1.3', '4.19.2.0', '4.19.3.0', '4.20.1.0']"
3434

3535
jobs:
3636
prepare-matrix:
@@ -54,7 +54,7 @@ jobs:
5454
with:
5555
go-version-file: 'go.mod'
5656
- name: Configure Cloudstack v${{ matrix.cloudstack-version }}
57-
uses: ./.github/workflows/setup-cloudstack
57+
uses: ./.github/actions/setup-cloudstack
5858
id: setup-cloudstack
5959
with:
6060
cloudstack-version: ${{ matrix.cloudstack-version }}
@@ -78,8 +78,8 @@ jobs:
7878
fail-fast: false
7979
matrix:
8080
terraform-version:
81-
- '1.8.*'
82-
- '1.9.*'
81+
- '1.11.*'
82+
- '1.12.*'
8383
cloudstack-version: ${{ fromJson(needs.prepare-matrix.outputs.cloudstack-versions) }}
8484

8585
acceptance-opentofu:
@@ -93,7 +93,7 @@ jobs:
9393
with:
9494
go-version-file: 'go.mod'
9595
- name: Configure Cloudstack v${{ matrix.cloudstack-version }}
96-
uses: ./.github/workflows/setup-cloudstack
96+
uses: ./.github/actions/setup-cloudstack
9797
id: setup-cloudstack
9898
with:
9999
cloudstack-version: ${{ matrix.cloudstack-version }}
@@ -116,8 +116,8 @@ jobs:
116116
fail-fast: false
117117
matrix:
118118
opentofu-version:
119-
- '1.6.*'
120-
- '1.7.*'
119+
- '1.8.*'
120+
- '1.9.*'
121121
cloudstack-version: ${{ fromJson(needs.prepare-matrix.outputs.cloudstack-versions) }}
122122

123123
all-jobs-passed: # Will succeed if it is skipped

cloudstack/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ func Provider() *schema.Provider {
9696
"cloudstack_affinity_group": resourceCloudStackAffinityGroup(),
9797
"cloudstack_attach_volume": resourceCloudStackAttachVolume(),
9898
"cloudstack_autoscale_vm_profile": resourceCloudStackAutoScaleVMProfile(),
99+
"cloudstack_configuration": resourceCloudStackConfiguration(),
99100
"cloudstack_disk": resourceCloudStackDisk(),
100101
"cloudstack_egress_firewall": resourceCloudStackEgressFirewall(),
101102
"cloudstack_firewall": resourceCloudStackFirewall(),
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
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+
"fmt"
24+
25+
"github.com/apache/cloudstack-go/v2/cloudstack"
26+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
27+
)
28+
29+
func resourceCloudStackConfiguration() *schema.Resource {
30+
return &schema.Resource{
31+
Create: resourceCloudStackConfigurationCreate,
32+
Read: resourceCloudStackConfigurationRead,
33+
Update: resourceCloudStackConfigurationUpdate,
34+
Delete: resourceCloudStackConfigurationDelete,
35+
Importer: &schema.ResourceImporter{
36+
State: importStatePassthrough,
37+
},
38+
39+
Schema: map[string]*schema.Schema{
40+
"name": {
41+
Description: "configuration by name",
42+
Type: schema.TypeString,
43+
Required: true,
44+
},
45+
"account_id": {
46+
Description: "the ID of the Account to update the parameter value for corresponding account",
47+
Type: schema.TypeString,
48+
Optional: true,
49+
},
50+
"cluster_id": {
51+
Description: "the ID of the Cluster to update the parameter value for corresponding cluster",
52+
Type: schema.TypeString,
53+
Optional: true,
54+
},
55+
"domain_id": {
56+
Description: "the ID of the Domain to update the parameter value for corresponding domain",
57+
Type: schema.TypeString,
58+
Optional: true,
59+
},
60+
"image_store_uuid": {
61+
Description: "the ID of the Image Store to update the parameter value for corresponding image store",
62+
Type: schema.TypeString,
63+
Optional: true,
64+
},
65+
"store_id": {
66+
Description: "the ID of the Storage pool to update the parameter value for corresponding storage pool",
67+
Type: schema.TypeString,
68+
Optional: true,
69+
},
70+
"value": {
71+
Description: "the value of the configuration",
72+
Type: schema.TypeString,
73+
Optional: true,
74+
},
75+
"zone_id": {
76+
Description: "the ID of the Zone to update the parameter value for corresponding zone",
77+
Type: schema.TypeString,
78+
Optional: true,
79+
},
80+
// computed
81+
"category": {
82+
Description: "configurations by category",
83+
Type: schema.TypeString,
84+
Computed: true,
85+
},
86+
"description": {
87+
Description: "the description of the configuration",
88+
Type: schema.TypeString,
89+
Computed: true,
90+
},
91+
"is_dynamic": {
92+
Description: "true if the configuration is dynamic",
93+
Type: schema.TypeBool,
94+
Computed: true,
95+
},
96+
"scope": {
97+
Description: "scope(zone/cluster/pool/account) of the parameter that needs to be updated",
98+
Type: schema.TypeString,
99+
Computed: true,
100+
},
101+
},
102+
}
103+
}
104+
105+
func resourceCloudStackConfigurationRead(d *schema.ResourceData, meta interface{}) error {
106+
cs := meta.(*cloudstack.CloudStackClient)
107+
p := cs.Configuration.NewListConfigurationsParams()
108+
109+
// required
110+
p.SetName(d.Id())
111+
112+
// optional
113+
if v, ok := d.GetOk("account_id"); ok {
114+
p.SetAccountid(v.(string))
115+
}
116+
if v, ok := d.GetOk("category"); ok {
117+
p.SetCategory(v.(string))
118+
}
119+
if v, ok := d.GetOk("cluster_id"); ok {
120+
p.SetClusterid(v.(string))
121+
}
122+
if v, ok := d.GetOk("domain_id"); ok {
123+
p.SetDomainid(v.(string))
124+
}
125+
if v, ok := d.GetOk("image_store_uuid"); ok {
126+
p.SetImagestoreuuid(v.(string))
127+
}
128+
if v, ok := d.GetOk("store_id"); ok {
129+
p.SetStorageid(v.(string))
130+
}
131+
if v, ok := d.GetOk("zone_id"); ok {
132+
p.SetZoneid(v.(string))
133+
}
134+
135+
cfg, err := cs.Configuration.ListConfigurations(p)
136+
if err != nil {
137+
return err
138+
}
139+
140+
found := false
141+
for _, v := range cfg.Configurations {
142+
if v.Name == d.Id() {
143+
d.Set("category", v.Category)
144+
d.Set("description", v.Description)
145+
d.Set("is_dynamic", v.Isdynamic)
146+
d.Set("name", v.Name)
147+
d.Set("value", v.Value)
148+
d.Set("scope", v.Scope)
149+
found = true
150+
}
151+
}
152+
153+
if !found {
154+
return fmt.Errorf("listConfiguration failed. no matching names found %s", d.Id())
155+
}
156+
157+
return nil
158+
159+
}
160+
161+
func resourceCloudStackConfigurationCreate(d *schema.ResourceData, meta interface{}) error {
162+
if v, ok := d.GetOk("name"); ok {
163+
d.SetId(v.(string))
164+
}
165+
166+
resourceCloudStackConfigurationUpdate(d, meta)
167+
168+
return nil
169+
170+
}
171+
172+
func resourceCloudStackConfigurationUpdate(d *schema.ResourceData, meta interface{}) error {
173+
cs := meta.(*cloudstack.CloudStackClient)
174+
p := cs.Configuration.NewUpdateConfigurationParams(d.Id())
175+
176+
// Optional
177+
if v, ok := d.GetOk("account_id"); ok {
178+
p.SetAccountid(v.(string))
179+
}
180+
if v, ok := d.GetOk("cluster_id"); ok {
181+
p.SetClusterid(v.(string))
182+
}
183+
if v, ok := d.GetOk("domain_id"); ok {
184+
p.SetDomainid(v.(string))
185+
}
186+
if v, ok := d.GetOk("image_store_uuid"); ok {
187+
p.SetImagestoreuuid(v.(string))
188+
}
189+
if v, ok := d.GetOk("store_id"); ok {
190+
p.SetStorageid(v.(string))
191+
}
192+
if v, ok := d.GetOk("value"); ok {
193+
p.SetValue(v.(string))
194+
}
195+
if v, ok := d.GetOk("zone_id"); ok {
196+
p.SetZoneid(v.(string))
197+
}
198+
199+
_, err := cs.Configuration.UpdateConfiguration(p)
200+
if err != nil {
201+
return err
202+
}
203+
204+
resourceCloudStackConfigurationRead(d, meta)
205+
206+
return nil
207+
}
208+
209+
func resourceCloudStackConfigurationDelete(d *schema.ResourceData, meta interface{}) error {
210+
cs := meta.(*cloudstack.CloudStackClient)
211+
p := cs.Configuration.NewResetConfigurationParams(d.Id())
212+
213+
// Optional
214+
if v, ok := d.GetOk("account_id"); ok {
215+
p.SetAccountid(v.(string))
216+
}
217+
if v, ok := d.GetOk("cluster_id"); ok {
218+
p.SetClusterid(v.(string))
219+
}
220+
if v, ok := d.GetOk("domain_id"); ok {
221+
p.SetDomainid(v.(string))
222+
}
223+
if v, ok := d.GetOk("image_store_uuid"); ok {
224+
p.SetImagestoreid(v.(string))
225+
}
226+
if v, ok := d.GetOk("store_id"); ok {
227+
p.SetStorageid(v.(string))
228+
}
229+
if v, ok := d.GetOk("zone_id"); ok {
230+
p.SetZoneid(v.(string))
231+
}
232+
233+
_, err := cs.Configuration.ResetConfiguration(p)
234+
if err != nil {
235+
return err
236+
}
237+
238+
return nil
239+
}

0 commit comments

Comments
 (0)