mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-30 10:47:14 -06:00
Merge pull request #809 from svanharmelen/f-provider/cloudstack-extend-features
provider/cloudstack: extend features and adding a new resource
This commit is contained in:
commit
e4867b6fbc
@ -38,6 +38,7 @@ func Provider() terraform.ResourceProvider {
|
||||
|
||||
ResourcesMap: map[string]*schema.Resource{
|
||||
"cloudstack_disk": resourceCloudStackDisk(),
|
||||
"cloudstack_egress_firewall": resourceCloudStackEgressFirewall(),
|
||||
"cloudstack_firewall": resourceCloudStackFirewall(),
|
||||
"cloudstack_instance": resourceCloudStackInstance(),
|
||||
"cloudstack_ipaddress": resourceCloudStackIPAddress(),
|
||||
|
@ -38,131 +38,9 @@ func testAccPreCheck(t *testing.T) {
|
||||
if v := os.Getenv("CLOUDSTACK_SECRET_KEY"); v == "" {
|
||||
t.Fatal("CLOUDSTACK_SECRET_KEY must be set for acceptance tests")
|
||||
}
|
||||
|
||||
// Testing all environment/installation specific variables which are needed
|
||||
// to run all the acceptance tests
|
||||
if CLOUDSTACK_DISK_OFFERING_1 == "" {
|
||||
if v := os.Getenv("CLOUDSTACK_DISK_OFFERING_1"); v == "" {
|
||||
t.Fatal("CLOUDSTACK_DISK_OFFERING_1 must be set for acceptance tests")
|
||||
} else {
|
||||
CLOUDSTACK_DISK_OFFERING_1 = v
|
||||
}
|
||||
}
|
||||
if CLOUDSTACK_DISK_OFFERING_2 == "" {
|
||||
if v := os.Getenv("CLOUDSTACK_DISK_OFFERING_2"); v == "" {
|
||||
t.Fatal("CLOUDSTACK_DISK_OFFERING_2 must be set for acceptance tests")
|
||||
} else {
|
||||
CLOUDSTACK_DISK_OFFERING_2 = v
|
||||
}
|
||||
}
|
||||
if CLOUDSTACK_SERVICE_OFFERING_1 == "" {
|
||||
if v := os.Getenv("CLOUDSTACK_SERVICE_OFFERING_1"); v == "" {
|
||||
t.Fatal("CLOUDSTACK_SERVICE_OFFERING_1 must be set for acceptance tests")
|
||||
} else {
|
||||
CLOUDSTACK_SERVICE_OFFERING_1 = v
|
||||
}
|
||||
}
|
||||
if CLOUDSTACK_SERVICE_OFFERING_2 == "" {
|
||||
if v := os.Getenv("CLOUDSTACK_SERVICE_OFFERING_2"); v == "" {
|
||||
t.Fatal("CLOUDSTACK_SERVICE_OFFERING_2 must be set for acceptance tests")
|
||||
} else {
|
||||
CLOUDSTACK_SERVICE_OFFERING_2 = v
|
||||
}
|
||||
}
|
||||
if CLOUDSTACK_NETWORK_1 == "" {
|
||||
if v := os.Getenv("CLOUDSTACK_NETWORK_1"); v == "" {
|
||||
t.Fatal("CLOUDSTACK_NETWORK_1 must be set for acceptance tests")
|
||||
} else {
|
||||
CLOUDSTACK_NETWORK_1 = v
|
||||
}
|
||||
}
|
||||
if CLOUDSTACK_NETWORK_1_CIDR == "" {
|
||||
if v := os.Getenv("CLOUDSTACK_NETWORK_1_CIDR"); v == "" {
|
||||
t.Fatal("CLOUDSTACK_NETWORK_1_CIDR must be set for acceptance tests")
|
||||
} else {
|
||||
CLOUDSTACK_NETWORK_1_CIDR = v
|
||||
}
|
||||
}
|
||||
if CLOUDSTACK_NETWORK_1_OFFERING == "" {
|
||||
if v := os.Getenv("CLOUDSTACK_NETWORK_1_OFFERING"); v == "" {
|
||||
t.Fatal("CLOUDSTACK_NETWORK_1_OFFERING must be set for acceptance tests")
|
||||
} else {
|
||||
CLOUDSTACK_NETWORK_1_OFFERING = v
|
||||
}
|
||||
}
|
||||
if CLOUDSTACK_NETWORK_1_IPADDRESS == "" {
|
||||
if v := os.Getenv("CLOUDSTACK_NETWORK_1_IPADDRESS"); v == "" {
|
||||
t.Fatal("CLOUDSTACK_NETWORK_1_IPADDRESS must be set for acceptance tests")
|
||||
} else {
|
||||
CLOUDSTACK_NETWORK_1_IPADDRESS = v
|
||||
}
|
||||
}
|
||||
if CLOUDSTACK_NETWORK_2 == "" {
|
||||
if v := os.Getenv("CLOUDSTACK_NETWORK_2"); v == "" {
|
||||
t.Fatal("CLOUDSTACK_NETWORK_2 must be set for acceptance tests")
|
||||
} else {
|
||||
CLOUDSTACK_NETWORK_2 = v
|
||||
}
|
||||
}
|
||||
if CLOUDSTACK_NETWORK_2_IPADDRESS == "" {
|
||||
if v := os.Getenv("CLOUDSTACK_NETWORK_2_IPADDRESS"); v == "" {
|
||||
t.Fatal("CLOUDSTACK_NETWORK_2_IPADDRESS must be set for acceptance tests")
|
||||
} else {
|
||||
CLOUDSTACK_NETWORK_2_IPADDRESS = v
|
||||
}
|
||||
}
|
||||
if CLOUDSTACK_VPC_CIDR == "" {
|
||||
if v := os.Getenv("CLOUDSTACK_VPC_CIDR"); v == "" {
|
||||
t.Fatal("CLOUDSTACK_VPC_CIDR must be set for acceptance tests")
|
||||
} else {
|
||||
CLOUDSTACK_VPC_CIDR = v
|
||||
}
|
||||
}
|
||||
if CLOUDSTACK_VPC_OFFERING == "" {
|
||||
if v := os.Getenv("CLOUDSTACK_VPC_OFFERING"); v == "" {
|
||||
t.Fatal("CLOUDSTACK_VPC_OFFERING must be set for acceptance tests")
|
||||
} else {
|
||||
CLOUDSTACK_VPC_OFFERING = v
|
||||
}
|
||||
}
|
||||
if CLOUDSTACK_VPC_NETWORK_CIDR == "" {
|
||||
if v := os.Getenv("CLOUDSTACK_VPC_NETWORK_CIDR"); v == "" {
|
||||
t.Fatal("CLOUDSTACK_VPC_NETWORK_CIDR must be set for acceptance tests")
|
||||
} else {
|
||||
CLOUDSTACK_VPC_NETWORK_CIDR = v
|
||||
}
|
||||
}
|
||||
if CLOUDSTACK_VPC_NETWORK_OFFERING == "" {
|
||||
if v := os.Getenv("CLOUDSTACK_VPC_NETWORK_OFFERING"); v == "" {
|
||||
t.Fatal("CLOUDSTACK_VPC_NETWORK_OFFERING must be set for acceptance tests")
|
||||
} else {
|
||||
CLOUDSTACK_VPC_NETWORK_OFFERING = v
|
||||
}
|
||||
}
|
||||
if CLOUDSTACK_PUBLIC_IPADDRESS == "" {
|
||||
if v := os.Getenv("CLOUDSTACK_PUBLIC_IPADDRESS"); v == "" {
|
||||
t.Fatal("CLOUDSTACK_PUBLIC_IPADDRESS must be set for acceptance tests")
|
||||
} else {
|
||||
CLOUDSTACK_PUBLIC_IPADDRESS = v
|
||||
}
|
||||
}
|
||||
if CLOUDSTACK_TEMPLATE == "" {
|
||||
if v := os.Getenv("CLOUDSTACK_TEMPLATE"); v == "" {
|
||||
t.Fatal("CLOUDSTACK_TEMPLATE must be set for acceptance tests")
|
||||
} else {
|
||||
CLOUDSTACK_TEMPLATE = v
|
||||
}
|
||||
}
|
||||
if CLOUDSTACK_ZONE == "" {
|
||||
if v := os.Getenv("CLOUDSTACK_ZONE"); v == "" {
|
||||
t.Fatal("CLOUDSTACK_ZONE must be set for acceptance tests")
|
||||
} else {
|
||||
CLOUDSTACK_ZONE = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EITHER SET THESE, OR ADD THE VALUES TO YOUR ENV!!
|
||||
// SET THESE VALUES IN ORDER TO RUN THE ACC TESTS!!
|
||||
var CLOUDSTACK_DISK_OFFERING_1 = ""
|
||||
var CLOUDSTACK_DISK_OFFERING_2 = ""
|
||||
var CLOUDSTACK_SERVICE_OFFERING_1 = ""
|
||||
|
@ -183,10 +183,7 @@ func resourceCloudStackDiskUpdate(d *schema.ResourceData, meta interface{}) erro
|
||||
}
|
||||
|
||||
// Create a new parameter struct
|
||||
p := cs.Volume.NewResizeVolumeParams()
|
||||
|
||||
// Set the volume UUID
|
||||
p.SetId(d.Id())
|
||||
p := cs.Volume.NewResizeVolumeParams(d.Id())
|
||||
|
||||
// Retrieve the disk_offering UUID
|
||||
diskofferingid, e := retrieveUUID(cs, "disk_offering", d.Get("disk_offering").(string))
|
||||
|
@ -0,0 +1,489 @@
|
||||
package cloudstack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/hashcode"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/xanzy/go-cloudstack/cloudstack"
|
||||
)
|
||||
|
||||
func resourceCloudStackEgressFirewall() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourceCloudStackEgressFirewallCreate,
|
||||
Read: resourceCloudStackEgressFirewallRead,
|
||||
Update: resourceCloudStackEgressFirewallUpdate,
|
||||
Delete: resourceCloudStackEgressFirewallDelete,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"network": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"managed": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
Default: false,
|
||||
},
|
||||
|
||||
"rule": &schema.Schema{
|
||||
Type: schema.TypeSet,
|
||||
Required: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"source_cidr": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
|
||||
"protocol": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
|
||||
"icmp_type": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"icmp_code": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"ports": &schema.Schema{
|
||||
Type: schema.TypeSet,
|
||||
Optional: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Set: func(v interface{}) int {
|
||||
return hashcode.String(v.(string))
|
||||
},
|
||||
},
|
||||
|
||||
"uuids": &schema.Schema{
|
||||
Type: schema.TypeMap,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Set: resourceCloudStackEgressFirewallRuleHash,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourceCloudStackEgressFirewallCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
cs := meta.(*cloudstack.CloudStackClient)
|
||||
|
||||
// Retrieve the network UUID
|
||||
networkid, e := retrieveUUID(cs, "network", d.Get("network").(string))
|
||||
if e != nil {
|
||||
return e.Error()
|
||||
}
|
||||
|
||||
// We need to set this upfront in order to be able to save a partial state
|
||||
d.SetId(networkid)
|
||||
|
||||
// Create all rules that are configured
|
||||
if rs := d.Get("rule").(*schema.Set); rs.Len() > 0 {
|
||||
|
||||
// Create an empty schema.Set to hold all rules
|
||||
rules := &schema.Set{
|
||||
F: resourceCloudStackEgressFirewallRuleHash,
|
||||
}
|
||||
|
||||
for _, rule := range rs.List() {
|
||||
// Create a single rule
|
||||
err := resourceCloudStackEgressFirewallCreateRule(d, meta, rule.(map[string]interface{}))
|
||||
|
||||
// We need to update this first to preserve the correct state
|
||||
rules.Add(rule)
|
||||
d.Set("rule", rules)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resourceCloudStackEgressFirewallRead(d, meta)
|
||||
}
|
||||
|
||||
func resourceCloudStackEgressFirewallCreateRule(
|
||||
d *schema.ResourceData, meta interface{}, rule map[string]interface{}) error {
|
||||
cs := meta.(*cloudstack.CloudStackClient)
|
||||
uuids := rule["uuids"].(map[string]interface{})
|
||||
|
||||
// Make sure all required rule parameters are there
|
||||
if err := verifyEgressFirewallRuleParams(d, rule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create a new parameter struct
|
||||
p := cs.Firewall.NewCreateEgressFirewallRuleParams(d.Id(), rule["protocol"].(string))
|
||||
|
||||
// Set the CIDR list
|
||||
p.SetCidrlist([]string{rule["source_cidr"].(string)})
|
||||
|
||||
// If the protocol is ICMP set the needed ICMP parameters
|
||||
if rule["protocol"].(string) == "icmp" {
|
||||
p.SetIcmptype(rule["icmp_type"].(int))
|
||||
p.SetIcmpcode(rule["icmp_code"].(int))
|
||||
|
||||
r, err := cs.Firewall.CreateEgressFirewallRule(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uuids["icmp"] = r.Id
|
||||
rule["uuids"] = uuids
|
||||
}
|
||||
|
||||
// If protocol is not ICMP, loop through all ports
|
||||
if rule["protocol"].(string) != "icmp" {
|
||||
if ps := rule["ports"].(*schema.Set); ps.Len() > 0 {
|
||||
|
||||
// Create an empty schema.Set to hold all processed ports
|
||||
ports := &schema.Set{
|
||||
F: func(v interface{}) int {
|
||||
return hashcode.String(v.(string))
|
||||
},
|
||||
}
|
||||
|
||||
for _, port := range ps.List() {
|
||||
re := regexp.MustCompile(`^(\d+)(?:-(\d+))?$`)
|
||||
m := re.FindStringSubmatch(port.(string))
|
||||
|
||||
startPort, err := strconv.Atoi(m[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
endPort := startPort
|
||||
if m[2] != "" {
|
||||
endPort, err = strconv.Atoi(m[2])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
p.SetStartport(startPort)
|
||||
p.SetEndport(endPort)
|
||||
|
||||
r, err := cs.Firewall.CreateEgressFirewallRule(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ports.Add(port)
|
||||
rule["ports"] = ports
|
||||
|
||||
uuids[port.(string)] = r.Id
|
||||
rule["uuids"] = uuids
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceCloudStackEgressFirewallRead(d *schema.ResourceData, meta interface{}) error {
|
||||
cs := meta.(*cloudstack.CloudStackClient)
|
||||
|
||||
// Create an empty schema.Set to hold all rules
|
||||
rules := &schema.Set{
|
||||
F: resourceCloudStackEgressFirewallRuleHash,
|
||||
}
|
||||
|
||||
if d.Get("managed").(bool) {
|
||||
// Read all rules...
|
||||
}
|
||||
|
||||
// Read all rules that are configured
|
||||
if rs := d.Get("rule").(*schema.Set); rs.Len() > 0 {
|
||||
for _, rule := range rs.List() {
|
||||
rule := rule.(map[string]interface{})
|
||||
uuids := rule["uuids"].(map[string]interface{})
|
||||
|
||||
if rule["protocol"].(string) == "icmp" {
|
||||
id, ok := uuids["icmp"]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// Get the rule
|
||||
r, count, err := cs.Firewall.GetEgressFirewallRuleByID(id.(string))
|
||||
// If the count == 0, there is no object found for this UUID
|
||||
if err != nil {
|
||||
if count == 0 {
|
||||
delete(uuids, "icmp")
|
||||
continue
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Update the values
|
||||
rule["source_cidr"] = r.Cidrlist
|
||||
rule["protocol"] = r.Protocol
|
||||
rule["icmp_type"] = r.Icmptype
|
||||
rule["icmp_code"] = r.Icmpcode
|
||||
rules.Add(rule)
|
||||
}
|
||||
|
||||
// If protocol is not ICMP, loop through all ports
|
||||
if rule["protocol"].(string) != "icmp" {
|
||||
if ps := rule["ports"].(*schema.Set); ps.Len() > 0 {
|
||||
|
||||
// Create an empty schema.Set to hold all ports
|
||||
ports := &schema.Set{
|
||||
F: func(v interface{}) int {
|
||||
return hashcode.String(v.(string))
|
||||
},
|
||||
}
|
||||
|
||||
// Loop through all ports and retrieve their info
|
||||
for _, port := range ps.List() {
|
||||
id, ok := uuids[port.(string)]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// Get the rule
|
||||
r, count, err := cs.Firewall.GetEgressFirewallRuleByID(id.(string))
|
||||
if err != nil {
|
||||
if count == 0 {
|
||||
delete(uuids, port.(string))
|
||||
continue
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Update the values
|
||||
rule["source_cidr"] = r.Cidrlist
|
||||
rule["protocol"] = r.Protocol
|
||||
ports.Add(port)
|
||||
}
|
||||
|
||||
// If there is at least one port found, add this rule to the rules set
|
||||
if ports.Len() > 0 {
|
||||
rule["ports"] = ports
|
||||
rules.Add(rule)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If this is a managed firewall, add all unknown rules into a single dummy rule
|
||||
if d.Get("managed").(bool) {
|
||||
// Get all the rules from the running environment
|
||||
p := cs.Firewall.NewListEgressFirewallRulesParams()
|
||||
p.SetNetworkid(d.Id())
|
||||
p.SetListall(true)
|
||||
|
||||
r, err := cs.Firewall.ListEgressFirewallRules(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add all UUIDs to the uuids map
|
||||
uuids := make(map[string]interface{})
|
||||
for _, r := range r.EgressFirewallRules {
|
||||
uuids[r.Id] = r.Id
|
||||
}
|
||||
|
||||
// Delete all expected UUIDs from the uuids map
|
||||
for _, rule := range rules.List() {
|
||||
rule := rule.(map[string]interface{})
|
||||
|
||||
for _, id := range rule["uuids"].(map[string]interface{}) {
|
||||
delete(uuids, id.(string))
|
||||
}
|
||||
}
|
||||
|
||||
if len(uuids) > 0 {
|
||||
// Make a dummy rule to hold all unknown UUIDs
|
||||
rule := map[string]interface{}{
|
||||
"source_cidr": "N/A",
|
||||
"protocol": "N/A",
|
||||
"uuids": uuids,
|
||||
}
|
||||
|
||||
// Add the dummy rule to the rules set
|
||||
rules.Add(rule)
|
||||
}
|
||||
}
|
||||
|
||||
if rules.Len() > 0 {
|
||||
d.Set("rule", rules)
|
||||
} else {
|
||||
d.SetId("")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceCloudStackEgressFirewallUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
// Check if the rule set as a whole has changed
|
||||
if d.HasChange("rule") {
|
||||
o, n := d.GetChange("rule")
|
||||
ors := o.(*schema.Set).Difference(n.(*schema.Set))
|
||||
nrs := n.(*schema.Set).Difference(o.(*schema.Set))
|
||||
|
||||
// Now first loop through all the old rules and delete any obsolete ones
|
||||
for _, rule := range ors.List() {
|
||||
// Delete the rule as it no longer exists in the config
|
||||
err := resourceCloudStackEgressFirewallDeleteRule(d, meta, rule.(map[string]interface{}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we save the state of the currently configured rules
|
||||
rules := o.(*schema.Set).Intersection(n.(*schema.Set))
|
||||
d.Set("rule", rules)
|
||||
|
||||
// Then loop through al the currently configured rules and create the new ones
|
||||
for _, rule := range nrs.List() {
|
||||
// When succesfully deleted, re-create it again if it still exists
|
||||
err := resourceCloudStackEgressFirewallCreateRule(
|
||||
d, meta, rule.(map[string]interface{}))
|
||||
|
||||
// We need to update this first to preserve the correct state
|
||||
rules.Add(rule)
|
||||
d.Set("rule", rules)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resourceCloudStackEgressFirewallRead(d, meta)
|
||||
}
|
||||
|
||||
func resourceCloudStackEgressFirewallDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
// Delete all rules
|
||||
if rs := d.Get("rule").(*schema.Set); rs.Len() > 0 {
|
||||
for _, rule := range rs.List() {
|
||||
// Delete a single rule
|
||||
err := resourceCloudStackEgressFirewallDeleteRule(d, meta, rule.(map[string]interface{}))
|
||||
|
||||
// We need to update this first to preserve the correct state
|
||||
d.Set("rule", rs)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceCloudStackEgressFirewallDeleteRule(
|
||||
d *schema.ResourceData, meta interface{}, rule map[string]interface{}) error {
|
||||
cs := meta.(*cloudstack.CloudStackClient)
|
||||
uuids := rule["uuids"].(map[string]interface{})
|
||||
|
||||
for k, id := range uuids {
|
||||
// We don't care about the count here, so just continue
|
||||
if k == "#" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Create the parameter struct
|
||||
p := cs.Firewall.NewDeleteEgressFirewallRuleParams(id.(string))
|
||||
|
||||
// Delete the rule
|
||||
if _, err := cs.Firewall.DeleteEgressFirewallRule(p); err != nil {
|
||||
|
||||
// This is a very poor way to be told the UUID does no longer exist :(
|
||||
if strings.Contains(err.Error(), fmt.Sprintf(
|
||||
"Invalid parameter id value=%s due to incorrect long value format, "+
|
||||
"or entity does not exist", id.(string))) {
|
||||
delete(uuids, k)
|
||||
continue
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete the UUID of this rule
|
||||
delete(uuids, k)
|
||||
}
|
||||
|
||||
// Update the UUIDs
|
||||
rule["uuids"] = uuids
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceCloudStackEgressFirewallRuleHash(v interface{}) int {
|
||||
var buf bytes.Buffer
|
||||
m := v.(map[string]interface{})
|
||||
buf.WriteString(fmt.Sprintf(
|
||||
"%s-%s-", m["source_cidr"].(string), m["protocol"].(string)))
|
||||
|
||||
if v, ok := m["icmp_type"]; ok {
|
||||
buf.WriteString(fmt.Sprintf("%d-", v.(int)))
|
||||
}
|
||||
|
||||
if v, ok := m["icmp_code"]; ok {
|
||||
buf.WriteString(fmt.Sprintf("%d-", v.(int)))
|
||||
}
|
||||
|
||||
// We need to make sure to sort the strings below so that we always
|
||||
// generate the same hash code no matter what is in the set.
|
||||
if v, ok := m["ports"]; ok {
|
||||
vs := v.(*schema.Set).List()
|
||||
s := make([]string, len(vs))
|
||||
|
||||
for i, raw := range vs {
|
||||
s[i] = raw.(string)
|
||||
}
|
||||
sort.Strings(s)
|
||||
|
||||
for _, v := range s {
|
||||
buf.WriteString(fmt.Sprintf("%s-", v))
|
||||
}
|
||||
}
|
||||
|
||||
return hashcode.String(buf.String())
|
||||
}
|
||||
|
||||
func verifyEgressFirewallRuleParams(d *schema.ResourceData, rule map[string]interface{}) error {
|
||||
protocol := rule["protocol"].(string)
|
||||
if protocol != "tcp" && protocol != "udp" && protocol != "icmp" {
|
||||
return fmt.Errorf(
|
||||
"%s is not a valid protocol. Valid options are 'tcp', 'udp' and 'icmp'", protocol)
|
||||
}
|
||||
|
||||
if protocol == "icmp" {
|
||||
if _, ok := rule["icmp_type"]; !ok {
|
||||
return fmt.Errorf(
|
||||
"Parameter icmp_type is a required parameter when using protocol 'icmp'")
|
||||
}
|
||||
if _, ok := rule["icmp_code"]; !ok {
|
||||
return fmt.Errorf(
|
||||
"Parameter icmp_code is a required parameter when using protocol 'icmp'")
|
||||
}
|
||||
} else {
|
||||
if _, ok := rule["ports"]; !ok {
|
||||
return fmt.Errorf(
|
||||
"Parameter port is a required parameter when using protocol 'tcp' or 'udp'")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,200 @@
|
||||
package cloudstack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/xanzy/go-cloudstack/cloudstack"
|
||||
)
|
||||
|
||||
func TestAccCloudStackEgressFirewall_basic(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckCloudStackEgressFirewallDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccCloudStackEgressFirewall_basic,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCloudStackEgressFirewallRulesExist("cloudstack_egress_firewall.foo"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"cloudstack_egress_firewall.foo", "network", CLOUDSTACK_NETWORK_1),
|
||||
resource.TestCheckResourceAttr(
|
||||
"cloudstack_egress_firewall.foo",
|
||||
"rule.411689741.source_cidr",
|
||||
CLOUDSTACK_NETWORK_1_IPADDRESS+"/32"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"cloudstack_egress_firewall.foo", "rule.411689741.protocol", "tcp"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"cloudstack_egress_firewall.foo", "rule.411689741.ports.#", "2"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"cloudstack_egress_firewall.foo", "rule.411689741.ports.1209010669", "1000-2000"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"cloudstack_egress_firewall.foo", "rule.411689741.ports.1889509032", "80"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccCloudStackEgressFirewall_update(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckCloudStackEgressFirewallDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccCloudStackEgressFirewall_basic,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCloudStackEgressFirewallRulesExist("cloudstack_egress_firewall.foo"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"cloudstack_egress_firewall.foo", "network", CLOUDSTACK_NETWORK_1),
|
||||
resource.TestCheckResourceAttr(
|
||||
"cloudstack_egress_firewall.foo", "rule.#", "1"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"cloudstack_egress_firewall.foo",
|
||||
"rule.411689741.source_cidr",
|
||||
CLOUDSTACK_NETWORK_1_IPADDRESS+"/32"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"cloudstack_egress_firewall.foo", "rule.411689741.protocol", "tcp"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"cloudstack_egress_firewall.foo", "rule.411689741.ports.#", "2"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"cloudstack_egress_firewall.foo", "rule.411689741.ports.1209010669", "1000-2000"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"cloudstack_egress_firewall.foo", "rule.411689741.ports.1889509032", "80"),
|
||||
),
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
Config: testAccCloudStackEgressFirewall_update,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCloudStackEgressFirewallRulesExist("cloudstack_egress_firewall.foo"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"cloudstack_egress_firewall.foo", "network", CLOUDSTACK_NETWORK_1),
|
||||
resource.TestCheckResourceAttr(
|
||||
"cloudstack_egress_firewall.foo", "rule.#", "2"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"cloudstack_egress_firewall.foo",
|
||||
"rule.411689741.source_cidr",
|
||||
CLOUDSTACK_NETWORK_1_IPADDRESS+"/32"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"cloudstack_egress_firewall.foo", "rule.411689741.protocol", "tcp"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"cloudstack_egress_firewall.foo", "rule.411689741.ports.#", "2"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"cloudstack_egress_firewall.foo", "rule.411689741.ports.1209010669", "1000-2000"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"cloudstack_egress_firewall.foo", "rule.411689741.ports.1889509032", "80"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"cloudstack_egress_firewall.foo",
|
||||
"rule.845479598.source_cidr",
|
||||
CLOUDSTACK_NETWORK_1_IPADDRESS+"/32"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"cloudstack_egress_firewall.foo", "rule.845479598.protocol", "tcp"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"cloudstack_egress_firewall.foo", "rule.845479598.ports.#", "1"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"cloudstack_egress_firewall.foo", "rule.845479598.ports.3638101695", "443"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckCloudStackEgressFirewallRulesExist(n string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No firewall ID is set")
|
||||
}
|
||||
|
||||
for k, uuid := range rs.Primary.Attributes {
|
||||
if !strings.Contains(k, ".uuids.") || strings.HasSuffix(k, ".uuids.#") {
|
||||
continue
|
||||
}
|
||||
|
||||
cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
|
||||
_, count, err := cs.Firewall.GetEgressFirewallRuleByID(uuid)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
return fmt.Errorf("Firewall rule for %s not found", k)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckCloudStackEgressFirewallDestroy(s *terraform.State) error {
|
||||
cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
|
||||
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "cloudstack_egress_firewall" {
|
||||
continue
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No instance ID is set")
|
||||
}
|
||||
|
||||
for k, uuid := range rs.Primary.Attributes {
|
||||
if !strings.Contains(k, ".uuids.") || strings.HasSuffix(k, ".uuids.#") {
|
||||
continue
|
||||
}
|
||||
|
||||
p := cs.Firewall.NewDeleteEgressFirewallRuleParams(uuid)
|
||||
_, err := cs.Firewall.DeleteEgressFirewallRule(p)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var testAccCloudStackEgressFirewall_basic = fmt.Sprintf(`
|
||||
resource "cloudstack_egress_firewall" "foo" {
|
||||
network = "%s"
|
||||
|
||||
rule {
|
||||
source_cidr = "%s/32"
|
||||
protocol = "tcp"
|
||||
ports = ["80", "1000-2000"]
|
||||
}
|
||||
}`,
|
||||
CLOUDSTACK_NETWORK_1,
|
||||
CLOUDSTACK_NETWORK_1_IPADDRESS)
|
||||
|
||||
var testAccCloudStackEgressFirewall_update = fmt.Sprintf(`
|
||||
resource "cloudstack_egress_firewall" "foo" {
|
||||
network = "%s"
|
||||
|
||||
rule {
|
||||
source_cidr = "%s/32"
|
||||
protocol = "tcp"
|
||||
ports = ["80", "1000-2000"]
|
||||
}
|
||||
|
||||
rule {
|
||||
source_cidr = "%s/32"
|
||||
protocol = "tcp"
|
||||
ports = ["443"]
|
||||
}
|
||||
}`,
|
||||
CLOUDSTACK_NETWORK_1,
|
||||
CLOUDSTACK_NETWORK_1_IPADDRESS,
|
||||
CLOUDSTACK_NETWORK_1_IPADDRESS)
|
@ -27,6 +27,12 @@ func resourceCloudStackFirewall() *schema.Resource {
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"managed": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
Default: false,
|
||||
},
|
||||
|
||||
"rule": &schema.Schema{
|
||||
Type: schema.TypeSet,
|
||||
Required: true,
|
||||
@ -85,7 +91,7 @@ func resourceCloudStackFirewallCreate(d *schema.ResourceData, meta interface{})
|
||||
}
|
||||
|
||||
// We need to set this upfront in order to be able to save a partial state
|
||||
d.SetId(d.Get("ipaddress").(string))
|
||||
d.SetId(ipaddressid)
|
||||
|
||||
// Create all rules that are configured
|
||||
if rs := d.Get("rule").(*schema.Set); rs.Len() > 0 {
|
||||
@ -97,7 +103,7 @@ func resourceCloudStackFirewallCreate(d *schema.ResourceData, meta interface{})
|
||||
|
||||
for _, rule := range rs.List() {
|
||||
// Create a single rule
|
||||
err := resourceCloudStackFirewallCreateRule(d, meta, ipaddressid, rule.(map[string]interface{}))
|
||||
err := resourceCloudStackFirewallCreateRule(d, meta, rule.(map[string]interface{}))
|
||||
|
||||
// We need to update this first to preserve the correct state
|
||||
rules.Add(rule)
|
||||
@ -113,17 +119,17 @@ func resourceCloudStackFirewallCreate(d *schema.ResourceData, meta interface{})
|
||||
}
|
||||
|
||||
func resourceCloudStackFirewallCreateRule(
|
||||
d *schema.ResourceData, meta interface{}, ipaddressid string, rule map[string]interface{}) error {
|
||||
d *schema.ResourceData, meta interface{}, rule map[string]interface{}) error {
|
||||
cs := meta.(*cloudstack.CloudStackClient)
|
||||
uuids := rule["uuids"].(map[string]interface{})
|
||||
|
||||
// Make sure all required parameters are there
|
||||
if err := verifyFirewallParams(d, rule); err != nil {
|
||||
// Make sure all required rule parameters are there
|
||||
if err := verifyFirewallRuleParams(d, rule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create a new parameter struct
|
||||
p := cs.Firewall.NewCreateFirewallRuleParams(ipaddressid, rule["protocol"].(string))
|
||||
p := cs.Firewall.NewCreateFirewallRuleParams(d.Id(), rule["protocol"].(string))
|
||||
|
||||
// Set the CIDR list
|
||||
p.SetCidrlist([]string{rule["source_cidr"].(string)})
|
||||
@ -274,6 +280,46 @@ func resourceCloudStackFirewallRead(d *schema.ResourceData, meta interface{}) er
|
||||
}
|
||||
}
|
||||
|
||||
// If this is a managed firewall, add all unknown rules into a single dummy rule
|
||||
if d.Get("managed").(bool) {
|
||||
// Get all the rules from the running environment
|
||||
p := cs.Firewall.NewListFirewallRulesParams()
|
||||
p.SetIpaddressid(d.Id())
|
||||
p.SetListall(true)
|
||||
|
||||
r, err := cs.Firewall.ListFirewallRules(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add all UUIDs to the uuids map
|
||||
uuids := make(map[string]interface{})
|
||||
for _, r := range r.FirewallRules {
|
||||
uuids[r.Id] = r.Id
|
||||
}
|
||||
|
||||
// Delete all expected UUIDs from the uuids map
|
||||
for _, rule := range rules.List() {
|
||||
rule := rule.(map[string]interface{})
|
||||
|
||||
for _, id := range rule["uuids"].(map[string]interface{}) {
|
||||
delete(uuids, id.(string))
|
||||
}
|
||||
}
|
||||
|
||||
if len(uuids) > 0 {
|
||||
// Make a dummy rule to hold all unknown UUIDs
|
||||
rule := map[string]interface{}{
|
||||
"source_cidr": "N/A",
|
||||
"protocol": "N/A",
|
||||
"uuids": uuids,
|
||||
}
|
||||
|
||||
// Add the dummy rule to the rules set
|
||||
rules.Add(rule)
|
||||
}
|
||||
}
|
||||
|
||||
if rules.Len() > 0 {
|
||||
d.Set("rule", rules)
|
||||
} else {
|
||||
@ -284,14 +330,6 @@ func resourceCloudStackFirewallRead(d *schema.ResourceData, meta interface{}) er
|
||||
}
|
||||
|
||||
func resourceCloudStackFirewallUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
cs := meta.(*cloudstack.CloudStackClient)
|
||||
|
||||
// Retrieve the ipaddress UUID
|
||||
ipaddressid, e := retrieveUUID(cs, "ipaddress", d.Get("ipaddress").(string))
|
||||
if e != nil {
|
||||
return e.Error()
|
||||
}
|
||||
|
||||
// Check if the rule set as a whole has changed
|
||||
if d.HasChange("rule") {
|
||||
o, n := d.GetChange("rule")
|
||||
@ -315,7 +353,7 @@ func resourceCloudStackFirewallUpdate(d *schema.ResourceData, meta interface{})
|
||||
for _, rule := range nrs.List() {
|
||||
// When succesfully deleted, re-create it again if it still exists
|
||||
err := resourceCloudStackFirewallCreateRule(
|
||||
d, meta, ipaddressid, rule.(map[string]interface{}))
|
||||
d, meta, rule.(map[string]interface{}))
|
||||
|
||||
// We need to update this first to preserve the correct state
|
||||
rules.Add(rule)
|
||||
@ -355,6 +393,11 @@ func resourceCloudStackFirewallDeleteRule(
|
||||
uuids := rule["uuids"].(map[string]interface{})
|
||||
|
||||
for k, id := range uuids {
|
||||
// We don't care about the count here, so just continue
|
||||
if k == "#" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Create the parameter struct
|
||||
p := cs.Firewall.NewDeleteFirewallRuleParams(id.(string))
|
||||
|
||||
@ -415,7 +458,7 @@ func resourceCloudStackFirewallRuleHash(v interface{}) int {
|
||||
return hashcode.String(buf.String())
|
||||
}
|
||||
|
||||
func verifyFirewallParams(d *schema.ResourceData, rule map[string]interface{}) error {
|
||||
func verifyFirewallRuleParams(d *schema.ResourceData, rule map[string]interface{}) error {
|
||||
protocol := rule["protocol"].(string)
|
||||
if protocol != "tcp" && protocol != "udp" && protocol != "icmp" {
|
||||
return fmt.Errorf(
|
||||
|
@ -111,7 +111,7 @@ func testAccCheckCloudStackFirewallRulesExist(n string) resource.TestCheckFunc {
|
||||
}
|
||||
|
||||
for k, uuid := range rs.Primary.Attributes {
|
||||
if !strings.Contains(k, "uuids") {
|
||||
if !strings.Contains(k, ".uuids.") || strings.HasSuffix(k, ".uuids.#") {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -144,7 +144,7 @@ func testAccCheckCloudStackFirewallDestroy(s *terraform.State) error {
|
||||
}
|
||||
|
||||
for k, uuid := range rs.Primary.Attributes {
|
||||
if !strings.Contains(k, "uuids") {
|
||||
if !strings.Contains(k, ".uuids.") || strings.HasSuffix(k, ".uuids.#") {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,8 @@ func verifyIPAddressParams(d *schema.ResourceData) error {
|
||||
_, vpc := d.GetOk("vpc")
|
||||
|
||||
if (network && vpc) || (!network && !vpc) {
|
||||
return fmt.Errorf("You must supply a value for either (so not both) the 'network' or 'vpc' argument")
|
||||
return fmt.Errorf(
|
||||
"You must supply a value for either (so not both) the 'network' or 'vpc' parameter")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -79,13 +79,13 @@ func resourceCloudStackNetworkCreate(d *schema.ResourceData, meta interface{}) e
|
||||
}
|
||||
|
||||
// Compute/set the display text
|
||||
displaytext := d.Get("display_text").(string)
|
||||
if displaytext == "" {
|
||||
displaytext, ok := d.GetOk("display_text")
|
||||
if !ok {
|
||||
displaytext = name
|
||||
}
|
||||
|
||||
// Create a new parameter struct
|
||||
p := cs.Network.NewCreateNetworkParams(displaytext, name, networkofferingid, zoneid)
|
||||
p := cs.Network.NewCreateNetworkParams(displaytext.(string), name, networkofferingid, zoneid)
|
||||
|
||||
// Get the network details from the CIDR
|
||||
m, err := parseCIDR(d.Get("cidr").(string))
|
||||
@ -147,7 +147,7 @@ func resourceCloudStackNetworkRead(d *schema.ResourceData, meta interface{}) err
|
||||
}
|
||||
|
||||
d.Set("name", n.Name)
|
||||
d.Set("display_test", n.Displaytext)
|
||||
d.Set("display_text", n.Displaytext)
|
||||
d.Set("cidr", n.Cidr)
|
||||
d.Set("network_offering", n.Networkofferingname)
|
||||
d.Set("zone", n.Zonename)
|
||||
|
@ -27,6 +27,12 @@ func resourceCloudStackNetworkACLRule() *schema.Resource {
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"managed": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
Default: false,
|
||||
},
|
||||
|
||||
"rule": &schema.Schema{
|
||||
Type: schema.TypeSet,
|
||||
Required: true,
|
||||
@ -295,6 +301,46 @@ func resourceCloudStackNetworkACLRuleRead(d *schema.ResourceData, meta interface
|
||||
}
|
||||
}
|
||||
|
||||
// If this is a managed firewall, add all unknown rules into a single dummy rule
|
||||
if d.Get("managed").(bool) {
|
||||
// Get all the rules from the running environment
|
||||
p := cs.NetworkACL.NewListNetworkACLsParams()
|
||||
p.SetAclid(d.Id())
|
||||
p.SetListall(true)
|
||||
|
||||
r, err := cs.NetworkACL.ListNetworkACLs(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add all UUIDs to the uuids map
|
||||
uuids := make(map[string]interface{})
|
||||
for _, r := range r.NetworkACLs {
|
||||
uuids[r.Id] = r.Id
|
||||
}
|
||||
|
||||
// Delete all expected UUIDs from the uuids map
|
||||
for _, rule := range rules.List() {
|
||||
rule := rule.(map[string]interface{})
|
||||
|
||||
for _, id := range rule["uuids"].(map[string]interface{}) {
|
||||
delete(uuids, id.(string))
|
||||
}
|
||||
}
|
||||
|
||||
if len(uuids) > 0 {
|
||||
// Make a dummy rule to hold all unknown UUIDs
|
||||
rule := map[string]interface{}{
|
||||
"source_cidr": "N/A",
|
||||
"protocol": "N/A",
|
||||
"uuids": uuids,
|
||||
}
|
||||
|
||||
// Add the dummy rule to the rules set
|
||||
rules.Add(rule)
|
||||
}
|
||||
}
|
||||
|
||||
if rules.Len() > 0 {
|
||||
d.Set("rule", rules)
|
||||
} else {
|
||||
@ -371,6 +417,11 @@ func resourceCloudStackNetworkACLRuleDeleteRule(
|
||||
uuids := rule["uuids"].(map[string]interface{})
|
||||
|
||||
for k, id := range uuids {
|
||||
// We don't care about the count here, so just continue
|
||||
if k == "#" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Create the parameter struct
|
||||
p := cs.NetworkACL.NewDeleteNetworkACLParams(id.(string))
|
||||
|
||||
@ -438,7 +489,7 @@ func resourceCloudStackNetworkACLRuleHash(v interface{}) int {
|
||||
func verifyNetworkACLRuleParams(d *schema.ResourceData, rule map[string]interface{}) error {
|
||||
action := rule["action"].(string)
|
||||
if action != "allow" && action != "deny" {
|
||||
return fmt.Errorf("Parameter action only excepts 'allow' or 'deny' as values")
|
||||
return fmt.Errorf("Parameter action only accepts 'allow' or 'deny' as values")
|
||||
}
|
||||
|
||||
protocol := rule["protocol"].(string)
|
||||
@ -469,7 +520,7 @@ func verifyNetworkACLRuleParams(d *schema.ResourceData, rule map[string]interfac
|
||||
traffic := rule["traffic_type"].(string)
|
||||
if traffic != "ingress" && traffic != "egress" {
|
||||
return fmt.Errorf(
|
||||
"Parameter traffic_type only excepts 'ingress' or 'egress' as values")
|
||||
"Parameter traffic_type only accepts 'ingress' or 'egress' as values")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -104,7 +104,7 @@ func TestAccCloudStackNetworkACLRule_update(t *testing.T) {
|
||||
resource.TestCheckResourceAttr(
|
||||
"cloudstack_network_acl_rule.foo", "rule.4267872693.ports.1889509032", "80"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"cloudstack_network_acl_rule.foo", "rule.4267872693.traffic_type", "engress"),
|
||||
"cloudstack_network_acl_rule.foo", "rule.4267872693.traffic_type", "egress"),
|
||||
),
|
||||
},
|
||||
},
|
||||
@ -123,7 +123,7 @@ func testAccCheckCloudStackNetworkACLRulesExist(n string) resource.TestCheckFunc
|
||||
}
|
||||
|
||||
for k, uuid := range rs.Primary.Attributes {
|
||||
if !strings.Contains(k, "uuids") {
|
||||
if !strings.Contains(k, ".uuids.") || strings.HasSuffix(k, ".uuids.#") {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -156,7 +156,7 @@ func testAccCheckCloudStackNetworkACLRuleDestroy(s *terraform.State) error {
|
||||
}
|
||||
|
||||
for k, uuid := range rs.Primary.Attributes {
|
||||
if !strings.Contains(k, "uuids") {
|
||||
if !strings.Contains(k, ".uuids.") || strings.HasSuffix(k, ".uuids.#") {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ func resourceCloudStackVPCCreate(d *schema.ResourceData, meta interface{}) error
|
||||
// Set the display text
|
||||
displaytext, ok := d.GetOk("display_text")
|
||||
if !ok {
|
||||
displaytext = d.Get("name")
|
||||
displaytext = name
|
||||
}
|
||||
|
||||
// Create a new parameter struct
|
||||
@ -103,7 +103,7 @@ func resourceCloudStackVPCRead(d *schema.ResourceData, meta interface{}) error {
|
||||
}
|
||||
|
||||
d.Set("name", v.Name)
|
||||
d.Set("display_test", v.Displaytext)
|
||||
d.Set("display_text", v.Displaytext)
|
||||
d.Set("cidr", v.Cidr)
|
||||
d.Set("zone", v.Zonename)
|
||||
|
||||
@ -124,7 +124,7 @@ func resourceCloudStackVPCUpdate(d *schema.ResourceData, meta interface{}) error
|
||||
// Check if the name or display text is changed
|
||||
if d.HasChange("name") || d.HasChange("display_text") {
|
||||
// Create a new parameter struct
|
||||
p := cs.VPC.NewUpdateVPCParams(d.Id(), d.Get("name").(string))
|
||||
p := cs.VPC.NewUpdateVPCParams(d.Id())
|
||||
|
||||
// Set the display text
|
||||
displaytext, ok := d.GetOk("display_text")
|
||||
|
Loading…
Reference in New Issue
Block a user