Skip to content

Commit f0b83ca

Browse files
authored
Adding new data sources to cloudstack-terraform-proivder (#38)
* create ssh keypair data-source * create a method stub for instance data-source * extending instance data-source to use its id as an input * use SetId() to define the ID of the instance resource * implement filters to allow filtering off of any exported attributes * add ip-address to schema and allow filtering off of it * create a method stub to acceptance test the instance data-source * basic acceptance test for instance data-source passing * adding comments to improve the readability of code * add basic acceptance test for ssh keypair data-source * create network offering resource * complete network offering resource type * create network offering data source * add basic acceptance test for network offering data source * create disk offering resource * implement create method for disk offering resource * create cloudstack volume resource type * create cloudstack zone resource type * create zone data source * add acceptance test for zone datasource * create service offering resource * create service offering data source * add service offering data source * fix typos in comments for network offering resource * add volume data source * add acceptance test for volume data source * create account resource * implement delete method in account resource * fix a typo in network offering * create user resource * create domain resource * add vpc data-source * add ip address data-source * add user data-source * add vpn connection data-source * add acceptance test for vpc data-source * add acceptance test for ipaddress data-source * add acceptance test for user data-source * fix a typo in applyNetworkOfferingFilters method of network offering data-source
1 parent 567f8ab commit f0b83ca

28 files changed

+3147
-1
lines changed
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
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+
"time"
29+
30+
"github.com/apache/cloudstack-go/v2/cloudstack"
31+
"github.com/hashicorp/terraform/helper/schema"
32+
)
33+
34+
func dataSourceCloudstackInstance() *schema.Resource {
35+
return &schema.Resource{
36+
Read: dataSourceCloudstackInstanceRead,
37+
Schema: map[string]*schema.Schema{
38+
"filter": dataSourceFiltersSchema(),
39+
40+
//Computed values
41+
"instance_id": {
42+
Type: schema.TypeString,
43+
Computed: true,
44+
},
45+
46+
"account": {
47+
Type: schema.TypeString,
48+
Computed: true,
49+
},
50+
51+
"display_name": {
52+
Type: schema.TypeString,
53+
Computed: true,
54+
},
55+
56+
"state": {
57+
Type: schema.TypeString,
58+
Computed: true,
59+
},
60+
61+
"host_id": {
62+
Type: schema.TypeString,
63+
Computed: true,
64+
},
65+
66+
"zone_id": {
67+
Type: schema.TypeString,
68+
Computed: true,
69+
},
70+
71+
"created": {
72+
Type: schema.TypeString,
73+
Computed: true,
74+
},
75+
76+
"tags": tagsSchema(),
77+
78+
"nic": {
79+
Type: schema.TypeList,
80+
Computed: true,
81+
Optional: true,
82+
Elem: &schema.Resource{
83+
Schema: map[string]*schema.Schema{
84+
"ip_address": {
85+
Type: schema.TypeString,
86+
Computed: true,
87+
Optional: true,
88+
},
89+
},
90+
},
91+
},
92+
},
93+
}
94+
}
95+
96+
func dataSourceCloudstackInstanceRead(d *schema.ResourceData, meta interface{}) error {
97+
log.Printf("Instance Data Source Read Started")
98+
99+
cs := meta.(*cloudstack.CloudStackClient)
100+
p := cs.VirtualMachine.NewListVirtualMachinesParams()
101+
csInstances, err := cs.VirtualMachine.ListVirtualMachines(p)
102+
103+
if err != nil {
104+
return fmt.Errorf("Failed to list instances: %s", err)
105+
}
106+
107+
filters := d.Get("filter")
108+
nic := d.Get("nic").([]interface{})
109+
var instances []*cloudstack.VirtualMachine
110+
111+
//the if-else block to check whether to filter the data source by an IP address
112+
// or by any other exported attributes
113+
if len(nic) != 0 {
114+
ip_address := nic[0].(map[string]interface{})["ip_address"]
115+
for _, i := range csInstances.VirtualMachines {
116+
if ip_address == i.Nic[0].Ipaddress {
117+
instances = append(instances, i)
118+
}
119+
}
120+
} else {
121+
for _, i := range csInstances.VirtualMachines {
122+
match, err := applyInstanceFilters(i, filters.(*schema.Set))
123+
if err != nil {
124+
return err
125+
}
126+
127+
if match {
128+
instances = append(instances, i)
129+
}
130+
}
131+
}
132+
133+
if len(instances) == 0 {
134+
return fmt.Errorf("No instance is matching with the specified regex")
135+
}
136+
//return the latest instance from the list of filtered instances according
137+
//to its creation date
138+
instance, err := latestInstance(instances)
139+
if err != nil {
140+
return err
141+
}
142+
log.Printf("[DEBUG] Selected instances: %s\n", instance.Displayname)
143+
144+
return instanceDescriptionAttributes(d, instance)
145+
}
146+
147+
func instanceDescriptionAttributes(d *schema.ResourceData, instance *cloudstack.VirtualMachine) error {
148+
d.SetId(instance.Id)
149+
d.Set("instance_id", instance.Id)
150+
d.Set("account", instance.Account)
151+
d.Set("created", instance.Created)
152+
d.Set("display_name", instance.Displayname)
153+
d.Set("state", instance.State)
154+
d.Set("host_id", instance.Hostid)
155+
d.Set("zone_id", instance.Zoneid)
156+
d.Set("nic", []interface{}{map[string]string{"ip_address": instance.Nic[0].Ipaddress}})
157+
158+
tags := make(map[string]interface{})
159+
for _, tag := range instance.Tags {
160+
tags[tag.Key] = tag.Value
161+
}
162+
d.Set("tags", tags)
163+
164+
return nil
165+
}
166+
167+
func latestInstance(instances []*cloudstack.VirtualMachine) (*cloudstack.VirtualMachine, error) {
168+
var latest time.Time
169+
var instance *cloudstack.VirtualMachine
170+
171+
for _, i := range instances {
172+
created, err := time.Parse("2006-01-02T15:04:05-0700", i.Created)
173+
if err != nil {
174+
return nil, fmt.Errorf("Failed to parse creation date of an instance: %s", err)
175+
}
176+
177+
if created.After(latest) {
178+
latest = created
179+
instance = i
180+
}
181+
}
182+
183+
return instance, nil
184+
}
185+
186+
func applyInstanceFilters(instance *cloudstack.VirtualMachine, filters *schema.Set) (bool, error) {
187+
var instanceJSON map[string]interface{}
188+
i, _ := json.Marshal(instance)
189+
err := json.Unmarshal(i, &instanceJSON)
190+
if err != nil {
191+
return false, err
192+
}
193+
194+
for _, f := range filters.List() {
195+
m := f.(map[string]interface{})
196+
r, err := regexp.Compile(m["value"].(string))
197+
if err != nil {
198+
return false, fmt.Errorf("Invalid regex: %s", err)
199+
}
200+
updatedName := strings.ReplaceAll(m["name"].(string), "_", "")
201+
instanceField := instanceJSON[updatedName].(string)
202+
if !r.MatchString(instanceField) {
203+
return false, nil
204+
}
205+
206+
}
207+
return true, nil
208+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
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/helper/resource"
26+
)
27+
28+
//basic acceptance to check if the display_name attribute has same value in
29+
//the created instance and its data source respectively.
30+
func TestAccInstanceDataSource_basic(t *testing.T) {
31+
resourceName := "cloudstack_instance.my_instance"
32+
datasourceName := "data.cloudstack_instance.my_instance_test"
33+
34+
resource.Test(t, resource.TestCase{
35+
PreCheck: func() { testAccPreCheck(t) },
36+
Providers: testAccProviders,
37+
Steps: []resource.TestStep{
38+
{
39+
Config: testAccInstanceDataSourceConfig_basic,
40+
Check: resource.ComposeTestCheckFunc(
41+
resource.TestCheckResourceAttrPair(datasourceName, "display_name", resourceName, "display_name"),
42+
),
43+
ExpectNonEmptyPlan: true,
44+
},
45+
},
46+
})
47+
}
48+
49+
const testAccInstanceDataSourceConfig_basic = `
50+
resource "cloudstack_instance" "my_instance" {
51+
name = "server-a"
52+
service_offering = "Small Instance"
53+
network_id = "b9c953a0-8686-4240-b8a4-43849f7079ff"
54+
template = "CentOS 5.5(64-bit) no GUI (KVM)"
55+
zone = "DC"
56+
}
57+
data "cloudstack_instance" "my_instance_test" {
58+
filter {
59+
name = "display_name"
60+
value = "server-a"
61+
}
62+
depends_on = [
63+
cloudstack_instance.my_instance
64+
]
65+
}
66+
`

0 commit comments

Comments
 (0)