Skip to content

Commit e62e4d4

Browse files
Improves service offering resource (apache#64)
1 parent 863e3bd commit e62e4d4

File tree

5 files changed

+315
-7
lines changed

5 files changed

+315
-7
lines changed

.github/workflows/testacc.yml

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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+
name: Acceptance Test
19+
20+
on: [push, pull_request]
21+
22+
concurrency:
23+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-testacc
24+
cancel-in-progress: true
25+
26+
jobs:
27+
testacc:
28+
name: Acceptance Test
29+
runs-on: ubuntu-22.04
30+
env:
31+
CLOUDSTACK_API_URL: http://localhost:8080/client/api
32+
steps:
33+
- uses: actions/checkout@v3
34+
- name: Set up Go
35+
uses: actions/setup-go@v3
36+
with:
37+
go-version: 1.19.x
38+
- name: Wait Cloudstack to be ready
39+
run: |
40+
echo "Starting Cloudstack health check"
41+
T=0
42+
until [ $T -gt 20 ] || curl -sfL http://localhost:8080 --output /dev/null
43+
do
44+
echo "Waiting for Cloudstack to be ready..."
45+
((T+=1))
46+
sleep 30
47+
done
48+
- name: Setting up Cloudstack
49+
run: |
50+
docker exec $(docker container ls --format=json -l | jq -r .ID) python /root/tools/marvin/marvin/deployDataCenter.py -i /root/setup/dev/advanced.cfg
51+
curl -sf --location "${CLOUDSTACK_API_URL}" \
52+
--header 'Content-Type: application/x-www-form-urlencoded' \
53+
--data-urlencode 'command=login' \
54+
--data-urlencode 'username=admin' \
55+
--data-urlencode 'password=password' \
56+
--data-urlencode 'response=json' \
57+
--data-urlencode 'domain=/' -j -c cookies.txt --output /dev/null
58+
59+
CLOUDSTACK_USER_ID=$(curl -fs "${CLOUDSTACK_API_URL}?command=listUsers&response=json" -b cookies.txt | jq -r '.listusersresponse.user[0].id')
60+
CLOUDSTACK_API_KEY=$(curl -s "${CLOUDSTACK_API_URL}?command=getUserKeys&id=${CLOUDSTACK_USER_ID}&response=json" -b cookies.txt | jq -r '.getuserkeysresponse.userkeys.apikey')
61+
CLOUDSTACK_SECRET_KEY=$(curl -fs "${CLOUDSTACK_API_URL}?command=getUserKeys&id=${CLOUDSTACK_USER_ID}&response=json" -b cookies.txt | jq -r '.getuserkeysresponse.userkeys.secretkey')
62+
63+
echo "::add-mask::$CLOUDSTACK_API_KEY"
64+
echo "::add-mask::$CLOUDSTACK_SECRET_KEY"
65+
66+
echo "CLOUDSTACK_API_KEY=$CLOUDSTACK_API_KEY" >> $GITHUB_ENV
67+
echo "CLOUDSTACK_SECRET_KEY=$CLOUDSTACK_SECRET_KEY" >> $GITHUB_ENV
68+
- name: Install CMK
69+
run: |
70+
curl -sfL https://github.com/apache/cloudstack-cloudmonkey/releases/download/6.3.0/cmk.linux.x86-64 -o /usr/local/bin/cmk
71+
chmod +x /usr/local/bin/cmk
72+
- name: Create extra resources
73+
run: |
74+
cmk -u $CLOUDSTACK_API_URL -k $CLOUDSTACK_API_KEY -s $CLOUDSTACK_SECRET_KEY -o json create project name=terraform displaytext=terraform
75+
- name: Run acceptance test
76+
run: |
77+
make testacc
78+
services:
79+
cloudstack-simulator:
80+
image: apache/cloudstack-simulator:${{ matrix.cloudstack_version }}
81+
ports:
82+
- 8080:5050
83+
strategy:
84+
matrix:
85+
cloudstack_version:
86+
- 4.17.2.0
87+
- 4.18.1.0
88+
- 4.19.0.0

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ When Docker started the container you can go to http://localhost:8080/client and
8888
Once the login page is shown and you can login, you need to provision a simulated data-center:
8989

9090
```sh
91-
$ docker exec -ti cloudstack python /root/tools/marvin/marvin/deployDataCenter.py -i /root/setup/dev/advanced.cfg
91+
docker exec -it cloudstack-simulator python /root/tools/marvin/marvin/deployDataCenter.py -i /root/setup/dev/advanced.cfg
9292
```
9393

9494
If you refresh the client or login again, you will now get passed the initial welcome screen and be able to go to your account details and retrieve the API key and secret. Export those together with the URL:

cloudstack/resource_cloudstack_service_offering.go

Lines changed: 117 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,61 @@ func resourceCloudStackServiceOffering() *schema.Resource {
4242
Type: schema.TypeString,
4343
Required: true,
4444
},
45+
"cpu_number": {
46+
Description: "Number of CPU cores",
47+
Type: schema.TypeInt,
48+
Optional: true,
49+
ForceNew: true,
50+
},
51+
"cpu_speed": {
52+
Description: "Speed of CPU in Mhz",
53+
Type: schema.TypeInt,
54+
Optional: true,
55+
ForceNew: true,
56+
},
57+
"host_tags": {
58+
Description: "The host tag for this service offering",
59+
Type: schema.TypeString,
60+
Optional: true,
61+
},
62+
"limit_cpu_use": {
63+
Description: "Restrict the CPU usage to committed service offering",
64+
Type: schema.TypeBool,
65+
Optional: true,
66+
ForceNew: true,
67+
Default: false,
68+
},
69+
"memory": {
70+
Description: "The total memory of the service offering in MB",
71+
Type: schema.TypeInt,
72+
Optional: true,
73+
ForceNew: true,
74+
},
75+
"offer_ha": {
76+
Description: "The HA for the service offering",
77+
Type: schema.TypeBool,
78+
Optional: true,
79+
ForceNew: true,
80+
Default: false,
81+
},
82+
"storage_type": {
83+
Description: "The storage type of the service offering. Values are local and shared",
84+
Type: schema.TypeString,
85+
Optional: true,
86+
ForceNew: true,
87+
Default: "shared",
88+
ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) {
89+
v := val.(string)
90+
91+
if v == "local" || v == "shared" {
92+
return
93+
}
94+
95+
errs = append(errs, fmt.Errorf("storage type should be either local or shared, got %s", v))
96+
97+
return
98+
},
99+
},
45100
},
46101
}
47102
}
@@ -53,6 +108,33 @@ func resourceCloudStackServiceOfferingCreate(d *schema.ResourceData, meta interf
53108

54109
// Create a new parameter struct
55110
p := cs.ServiceOffering.NewCreateServiceOfferingParams(display_text, name)
111+
if v, ok := d.GetOk("cpu_number"); ok {
112+
p.SetCpunumber(v.(int))
113+
}
114+
115+
if v, ok := d.GetOk("cpu_speed"); ok {
116+
p.SetCpuspeed(v.(int))
117+
}
118+
119+
if v, ok := d.GetOk("host_tags"); ok {
120+
p.SetHosttags(v.(string))
121+
}
122+
123+
if v, ok := d.GetOk("limit_cpu_use"); ok {
124+
p.SetLimitcpuuse(v.(bool))
125+
}
126+
127+
if v, ok := d.GetOk("memory"); ok {
128+
p.SetMemory(v.(int))
129+
}
130+
131+
if v, ok := d.GetOk("offer_ha"); ok {
132+
p.SetOfferha(v.(bool))
133+
}
134+
135+
if v, ok := d.GetOk("storage_type"); ok {
136+
p.SetStoragetype(v.(string))
137+
}
56138

57139
log.Printf("[DEBUG] Creating Service Offering %s", name)
58140
s, err := cs.ServiceOffering.CreateServiceOffering(p)
@@ -84,8 +166,22 @@ func resourceCloudStackServiceOfferingRead(d *schema.ResourceData, meta interfac
84166
}
85167

86168
d.SetId(s.Id)
87-
d.Set("name", s.Name)
88-
d.Set("display_text", s.Displaytext)
169+
170+
fields := map[string]interface{}{
171+
"name": s.Name,
172+
"display_text": s.Displaytext,
173+
"cpu_number": s.Cpunumber,
174+
"cpu_speed": s.Cpuspeed,
175+
"host_tags": s.Hosttags,
176+
"limit_cpu_use": s.Limitcpuuse,
177+
"memory": s.Memory,
178+
"offer_ha": s.Offerha,
179+
"storage_type": s.Storagetype,
180+
}
181+
182+
for k, v := range fields {
183+
d.Set(k, v)
184+
}
89185

90186
return nil
91187
}
@@ -136,6 +232,25 @@ func resourceCloudStackServiceOfferingUpdate(d *schema.ResourceData, meta interf
136232
d.SetPartial("display_text")
137233
}
138234

235+
if d.HasChange("host_tags") {
236+
log.Printf("[DEBUG] Host tags changed for %s, starting update", name)
237+
238+
// Create a new parameter struct
239+
p := cs.ServiceOffering.NewUpdateServiceOfferingParams(d.Id())
240+
241+
// Set the new host tags
242+
p.SetHosttags(d.Get("host_tags").(string))
243+
244+
// Update the host tags
245+
_, err := cs.ServiceOffering.UpdateServiceOffering(p)
246+
if err != nil {
247+
return fmt.Errorf(
248+
"Error updating the host tags for service offering %s: %s", name, err)
249+
}
250+
251+
d.SetPartial("host_tags")
252+
}
253+
139254
d.Partial(false)
140255

141256
return resourceCloudStackServiceOfferingRead(d, meta)
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
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+
"testing"
25+
26+
"github.com/apache/cloudstack-go/v2/cloudstack"
27+
"github.com/hashicorp/terraform/helper/resource"
28+
"github.com/hashicorp/terraform/terraform"
29+
)
30+
31+
func TestAccCloudStackServiceOffering_basic(t *testing.T) {
32+
var so cloudstack.ServiceOffering
33+
resource.Test(t, resource.TestCase{
34+
PreCheck: func() { testAccPreCheck(t) },
35+
Providers: testAccProviders,
36+
Steps: []resource.TestStep{
37+
{
38+
Config: testAccCloudStackServiceOffering_basic,
39+
Check: resource.ComposeTestCheckFunc(
40+
testAccCheckCloudStackServiceOfferingExists("cloudstack_service_offering.test1", &so),
41+
resource.TestCheckResourceAttr("cloudstack_service_offering.test1", "cpu_number", "2"),
42+
resource.TestCheckResourceAttr("cloudstack_service_offering.test1", "cpu_speed", "2200"),
43+
resource.TestCheckResourceAttr("cloudstack_service_offering.test1", "memory", "8096"),
44+
),
45+
},
46+
},
47+
})
48+
}
49+
50+
const testAccCloudStackServiceOffering_basic = `
51+
resource "cloudstack_service_offering" "test1" {
52+
name = "service_offering_1"
53+
display_text = "Test"
54+
cpu_number = 2
55+
cpu_speed = 2200
56+
memory = 8096
57+
}
58+
`
59+
60+
func testAccCheckCloudStackServiceOfferingExists(n string, so *cloudstack.ServiceOffering) resource.TestCheckFunc {
61+
return func(s *terraform.State) error {
62+
rs, ok := s.RootModule().Resources[n]
63+
if !ok {
64+
return fmt.Errorf("Not found: %s", n)
65+
}
66+
67+
if rs.Primary.ID == "" {
68+
return fmt.Errorf("No service offering ID is set")
69+
}
70+
71+
cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
72+
resp, _, err := cs.ServiceOffering.GetServiceOfferingByID(rs.Primary.ID)
73+
if err != nil {
74+
return err
75+
}
76+
77+
if resp.Id != rs.Primary.ID {
78+
return fmt.Errorf("Service offering not found")
79+
}
80+
81+
*so = *resp
82+
83+
return nil
84+
}
85+
}

website/docs/r/service_offering.html.markdown

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,36 @@ resource "cloudstack_service_offering" "example" {
2424

2525
The following arguments are supported:
2626

27-
* `name` - (Required) The name of the service offering.
28-
* `display_text` - (Required) The display text of the service offering.
27+
* `name` - (Required) Name of the service offering.
28+
Changing this forces a new resource to be created.
29+
30+
* `display_text` - (Optional) The display text of the service offering.
31+
32+
* `cpu_number` - (Optional) The number of CPU cores.
33+
Changing this forces a new resource to be created.
34+
35+
* `cpu_speed` - (Optional) The speed of the CPU in Mhz.
36+
Changing this forces a new resource to be created.
37+
38+
* `memory` - (Optional) Memory reserved by the VM in MB.
39+
Changing this forces a new resource to be created.
40+
41+
* `host_tags` - (Optional) The host tags for the service offering.
42+
43+
* `limit_cpu_use` - (Optional) Restrict the CPU usage to committed service offering.
44+
Changing this forces a new resource to be created.
45+
46+
* `offer_ha` - (Optional) The HA for the service offering.
47+
Changing this forces a new resource to be created.
48+
49+
* `storage_type` - (Optional) The storage type of the service offering. Values are `local` and `shared`.
50+
Changing this forces a new resource to be created.
2951

3052
## Attributes Reference
3153

3254
The following attributes are exported:
3355

3456
* `id` - The ID of the service offering.
35-
* `name` - The name of the service offering.
36-
* `display_text` - The display text of the service offering.
3757

3858
## Import
3959

0 commit comments

Comments
 (0)