Skip to content

Commit 09df0ef

Browse files
committed
provider/google: Support Import of 'google_compute_firewall'
1 parent 782b248 commit 09df0ef

5 files changed

Lines changed: 241 additions & 14 deletions
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package google
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/hashicorp/terraform/helper/acctest"
8+
"github.com/hashicorp/terraform/helper/resource"
9+
)
10+
11+
func TestAccComputeFirewall_importBasic(t *testing.T) {
12+
resourceName := "google_compute_firewall.foobar"
13+
networkName := fmt.Sprintf("firewall-test-%s", acctest.RandString(10))
14+
firewallName := fmt.Sprintf("firewall-test-%s", acctest.RandString(10))
15+
16+
resource.Test(t, resource.TestCase{
17+
PreCheck: func() { testAccPreCheck(t) },
18+
Providers: testAccProviders,
19+
CheckDestroy: testAccCheckComputeFirewallDestroy,
20+
Steps: []resource.TestStep{
21+
resource.TestStep{
22+
Config: testAccComputeFirewall_basic(networkName, firewallName),
23+
},
24+
25+
resource.TestStep{
26+
ResourceName: resourceName,
27+
ImportState: true,
28+
ImportStateVerify: true,
29+
},
30+
},
31+
})
32+
}

builtin/providers/google/resource_compute_firewall.go

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"log"
77
"sort"
8+
"strings"
89

910
"github.com/hashicorp/terraform/helper/hashcode"
1011
"github.com/hashicorp/terraform/helper/schema"
@@ -18,6 +19,10 @@ func resourceComputeFirewall() *schema.Resource {
1819
Read: resourceComputeFirewallRead,
1920
Update: resourceComputeFirewallUpdate,
2021
Delete: resourceComputeFirewallDelete,
22+
Importer: &schema.ResourceImporter{
23+
State: schema.ImportStatePassthrough,
24+
},
25+
SchemaVersion: 1,
2126

2227
Schema: map[string]*schema.Schema{
2328
"name": &schema.Schema{
@@ -43,10 +48,9 @@ func resourceComputeFirewall() *schema.Resource {
4348
},
4449

4550
"ports": &schema.Schema{
46-
Type: schema.TypeSet,
51+
Type: schema.TypeList,
4752
Optional: true,
4853
Elem: &schema.Schema{Type: schema.TypeString},
49-
Set: schema.HashString,
5054
},
5155
},
5256
},
@@ -62,6 +66,7 @@ func resourceComputeFirewall() *schema.Resource {
6266
Type: schema.TypeString,
6367
Optional: true,
6468
ForceNew: true,
69+
Computed: true,
6570
},
6671

6772
"self_link": &schema.Schema{
@@ -101,11 +106,7 @@ func resourceComputeFirewallAllowHash(v interface{}) int {
101106
// We need to make sure to sort the strings below so that we always
102107
// generate the same hash code no matter what is in the set.
103108
if v, ok := m["ports"]; ok {
104-
vs := v.(*schema.Set).List()
105-
s := make([]string, len(vs))
106-
for i, raw := range vs {
107-
s[i] = raw.(string)
108-
}
109+
s := convertStringArr(v.([]interface{}))
109110
sort.Strings(s)
110111

111112
for _, v := range s {
@@ -146,6 +147,18 @@ func resourceComputeFirewallCreate(d *schema.ResourceData, meta interface{}) err
146147
return resourceComputeFirewallRead(d, meta)
147148
}
148149

150+
func flattenAllowed(allowed []*compute.FirewallAllowed) []map[string]interface{} {
151+
result := make([]map[string]interface{}, 0, len(allowed))
152+
for _, allow := range allowed {
153+
allowMap := make(map[string]interface{})
154+
allowMap["protocol"] = allow.IPProtocol
155+
allowMap["ports"] = allow.Ports
156+
157+
result = append(result, allowMap)
158+
}
159+
return result
160+
}
161+
149162
func resourceComputeFirewallRead(d *schema.ResourceData, meta interface{}) error {
150163
config := meta.(*Config)
151164

@@ -168,8 +181,16 @@ func resourceComputeFirewallRead(d *schema.ResourceData, meta interface{}) error
168181
return fmt.Errorf("Error reading firewall: %s", err)
169182
}
170183

184+
networkUrl := strings.Split(firewall.Network, "/")
171185
d.Set("self_link", firewall.SelfLink)
172-
186+
d.Set("name", firewall.Name)
187+
d.Set("network", networkUrl[len(networkUrl)-1])
188+
d.Set("description", firewall.Description)
189+
d.Set("project", project)
190+
d.Set("source_ranges", firewall.SourceRanges)
191+
d.Set("source_tags", firewall.SourceTags)
192+
d.Set("target_tags", firewall.TargetTags)
193+
d.Set("allow", flattenAllowed(firewall.Allowed))
173194
return nil
174195
}
175196

@@ -250,10 +271,10 @@ func resourceFirewall(
250271
m := v.(map[string]interface{})
251272

252273
var ports []string
253-
if v := m["ports"].(*schema.Set); v.Len() > 0 {
254-
ports = make([]string, v.Len())
255-
for i, v := range v.List() {
256-
ports[i] = v.(string)
274+
if v := convertStringArr(m["ports"].([]interface{})); len(v) > 0 {
275+
ports = make([]string, len(v))
276+
for i, v := range v {
277+
ports[i] = v
257278
}
258279
}
259280

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package google
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"sort"
7+
"strconv"
8+
"strings"
9+
10+
"github.com/hashicorp/terraform/terraform"
11+
)
12+
13+
func resourceComputeFirewallMigrateState(
14+
v int, is *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) {
15+
if is.Empty() {
16+
log.Println("[DEBUG] Empty FirewallState; nothing to migrate.")
17+
return is, nil
18+
}
19+
20+
switch v {
21+
case 0:
22+
log.Println("[INFO] Found Compute Firewall State v0; migrating to v1")
23+
is, err := migrateFirewallStateV0toV1(is)
24+
if err != nil {
25+
return is, err
26+
}
27+
return is, nil
28+
default:
29+
return is, fmt.Errorf("Unexpected schema version: %d", v)
30+
}
31+
}
32+
33+
func migrateFirewallStateV0toV1(is *terraform.InstanceState) (*terraform.InstanceState, error) {
34+
log.Printf("[DEBUG] Attributes before migration: %#v", is.Attributes)
35+
idx := 0
36+
portCount := 0
37+
newPorts := make(map[string]string)
38+
keys := make([]string, len(is.Attributes))
39+
for k, _ := range is.Attributes {
40+
keys[idx] = k
41+
idx++
42+
43+
}
44+
sort.Strings(keys)
45+
for _, k := range keys {
46+
if !strings.HasPrefix(k, "allow.") {
47+
continue
48+
}
49+
50+
if k == "allow.#" {
51+
continue
52+
}
53+
54+
if strings.HasSuffix(k, ".ports.#") {
55+
continue
56+
}
57+
58+
if strings.HasSuffix(k, ".protocol") {
59+
continue
60+
}
61+
62+
// We have a key that looks like "allow.<hash>.ports.*" and we know it's not
63+
// allow.<hash>.ports.# because we deleted it above, so it must be allow.<hash1>.ports.<hash2>
64+
// from the Set of Ports. Just need to convert it to a list by
65+
// replacing second hash with sequential numbers.
66+
kParts := strings.Split(k, ".")
67+
68+
// Sanity check: all four parts should be there and <hash> should be a number
69+
badFormat := false
70+
if len(kParts) != 4 {
71+
badFormat = true
72+
} else if _, err := strconv.Atoi(kParts[1]); err != nil {
73+
badFormat = true
74+
}
75+
76+
if badFormat {
77+
return is, fmt.Errorf(
78+
"migration error: found port key in unexpected format: %s", k)
79+
}
80+
allowHash, _ := strconv.Atoi(kParts[1])
81+
newK := fmt.Sprintf("allow.%d.ports.%d", allowHash, portCount)
82+
portCount++
83+
newPorts[newK] = is.Attributes[k]
84+
delete(is.Attributes, k)
85+
}
86+
87+
for k, v := range newPorts {
88+
is.Attributes[k] = v
89+
}
90+
91+
log.Printf("[DEBUG] Attributes after migration: %#v", is.Attributes)
92+
return is, nil
93+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package google
2+
3+
import (
4+
"testing"
5+
6+
"github.com/hashicorp/terraform/terraform"
7+
)
8+
9+
func TestComputeFirewallMigrateState(t *testing.T) {
10+
cases := map[string]struct {
11+
StateVersion int
12+
Attributes map[string]string
13+
Expected map[string]string
14+
Meta interface{}
15+
}{
16+
"change scope from list to set": {
17+
StateVersion: 0,
18+
Attributes: map[string]string{
19+
"allow.#": "1",
20+
"allow.0.protocol": "udp",
21+
"allow.0.ports.#": "4",
22+
"allow.0.ports.1693978638": "8080",
23+
"allow.0.ports.172152165": "8081",
24+
"allow.0.ports.299962681": "7072",
25+
"allow.0.ports.3435931483": "4044",
26+
},
27+
Expected: map[string]string{
28+
"allow.#": "1",
29+
"allow.0.protocol": "udp",
30+
"allow.0.ports.#": "4",
31+
"allow.0.ports.0": "8080",
32+
"allow.0.ports.1": "8081",
33+
"allow.0.ports.2": "7072",
34+
"allow.0.ports.3": "4044",
35+
},
36+
},
37+
}
38+
for tn, tc := range cases {
39+
is := &terraform.InstanceState{
40+
ID: "i-abc123",
41+
Attributes: tc.Attributes,
42+
}
43+
is, err := resourceComputeFirewallMigrateState(
44+
tc.StateVersion, is, tc.Meta)
45+
46+
if err != nil {
47+
t.Fatalf("bad: %s, err: %#v", tn, err)
48+
}
49+
50+
for k, v := range tc.Expected {
51+
if is.Attributes[k] != v {
52+
t.Fatalf(
53+
"bad: %s\n\n expected: %#v -> %#v\n got: %#v -> %#v\n in: %#v",
54+
tn, k, v, k, is.Attributes[k], is.Attributes)
55+
}
56+
}
57+
}
58+
}
59+
60+
func TestComputeFirewallMigrateState_empty(t *testing.T) {
61+
var is *terraform.InstanceState
62+
var meta interface{}
63+
64+
// should handle nil
65+
is, err := resourceComputeFirewallMigrateState(0, is, meta)
66+
67+
if err != nil {
68+
t.Fatalf("err: %#v", err)
69+
}
70+
if is != nil {
71+
t.Fatalf("expected nil instancestate, got: %#v", is)
72+
}
73+
74+
// should handle non-nil but empty
75+
is = &terraform.InstanceState{}
76+
is, err = resourceComputeFirewallMigrateState(0, is, meta)
77+
78+
if err != nil {
79+
t.Fatalf("err: %#v", err)
80+
}
81+
}

builtin/providers/google/resource_compute_firewall_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ func testAccCheckComputeFirewallPorts(
126126
func testAccComputeFirewall_basic(network, firewall string) string {
127127
return fmt.Sprintf(`
128128
resource "google_compute_network" "foobar" {
129-
name = "firewall-test-%s"
129+
name = "%s"
130130
ipv4_range = "10.0.0.0/16"
131131
}
132132
@@ -145,7 +145,7 @@ func testAccComputeFirewall_basic(network, firewall string) string {
145145
func testAccComputeFirewall_update(network, firewall string) string {
146146
return fmt.Sprintf(`
147147
resource "google_compute_network" "foobar" {
148-
name = "firewall-test-%s"
148+
name = "%s"
149149
ipv4_range = "10.0.0.0/16"
150150
}
151151

0 commit comments

Comments
 (0)