-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Beta support & Beta feature deny to google_compute_firewall #282
Changes from all commits
4d32061
d592381
2c5a956
80653aa
1eb861c
0396ce9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,9 +8,14 @@ import ( | |
|
||
"github.com/hashicorp/terraform/helper/hashcode" | ||
"github.com/hashicorp/terraform/helper/schema" | ||
|
||
computeBeta "google.golang.org/api/compute/v0.beta" | ||
"google.golang.org/api/compute/v1" | ||
) | ||
|
||
var FirewallBaseApiVersion = v1 | ||
var FirewallVersionedFeatures = []Feature{Feature{Version: v0beta, Item: "deny"}} | ||
|
||
func resourceComputeFirewall() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceComputeFirewallCreate, | ||
|
@@ -37,8 +42,9 @@ func resourceComputeFirewall() *schema.Resource { | |
}, | ||
|
||
"allow": { | ||
Type: schema.TypeSet, | ||
Required: true, | ||
Type: schema.TypeSet, | ||
Optional: true, | ||
ConflictsWith: []string{"deny"}, | ||
Elem: &schema.Resource{ | ||
Schema: map[string]*schema.Schema{ | ||
"protocol": { | ||
|
@@ -53,7 +59,33 @@ func resourceComputeFirewall() *schema.Resource { | |
}, | ||
}, | ||
}, | ||
Set: resourceComputeFirewallAllowHash, | ||
Set: resourceComputeFirewallRuleHash, | ||
}, | ||
|
||
"deny": { | ||
Type: schema.TypeSet, | ||
Optional: true, | ||
ConflictsWith: []string{"allow"}, | ||
Elem: &schema.Resource{ | ||
Schema: map[string]*schema.Schema{ | ||
"protocol": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
|
||
"ports": { | ||
Type: schema.TypeList, | ||
Optional: true, | ||
Elem: &schema.Schema{Type: schema.TypeString}, | ||
ForceNew: true, | ||
}, | ||
}, | ||
}, | ||
Set: resourceComputeFirewallRuleHash, | ||
|
||
// Unlike allow, deny can't be updated upstream | ||
ForceNew: true, | ||
}, | ||
|
||
"description": { | ||
|
@@ -98,7 +130,7 @@ func resourceComputeFirewall() *schema.Resource { | |
} | ||
} | ||
|
||
func resourceComputeFirewallAllowHash(v interface{}) int { | ||
func resourceComputeFirewallRuleHash(v interface{}) int { | ||
var buf bytes.Buffer | ||
m := v.(map[string]interface{}) | ||
buf.WriteString(fmt.Sprintf("%s-", m["protocol"].(string))) | ||
|
@@ -118,36 +150,57 @@ func resourceComputeFirewallAllowHash(v interface{}) int { | |
} | ||
|
||
func resourceComputeFirewallCreate(d *schema.ResourceData, meta interface{}) error { | ||
computeApiVersion := getComputeApiVersion(d, FirewallBaseApiVersion, FirewallVersionedFeatures) | ||
config := meta.(*Config) | ||
|
||
project, err := getProject(d, config) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
firewall, err := resourceFirewall(d, meta) | ||
firewall, err := resourceFirewall(d, meta, computeApiVersion) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
op, err := config.clientCompute.Firewalls.Insert( | ||
project, firewall).Do() | ||
if err != nil { | ||
return fmt.Errorf("Error creating firewall: %s", err) | ||
var op interface{} | ||
switch computeApiVersion { | ||
case v1: | ||
firewallV1 := &compute.Firewall{} | ||
err := Convert(firewall, firewallV1) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
op, err = config.clientCompute.Firewalls.Insert(project, firewallV1).Do() | ||
if err != nil { | ||
return fmt.Errorf("Error creating firewall: %s", err) | ||
} | ||
case v0beta: | ||
firewallV0Beta := &computeBeta.Firewall{} | ||
err := Convert(firewall, firewallV0Beta) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
op, err = config.clientComputeBeta.Firewalls.Insert(project, firewallV0Beta).Do() | ||
if err != nil { | ||
return fmt.Errorf("Error creating firewall: %s", err) | ||
} | ||
} | ||
|
||
// It probably maybe worked, so store the ID now | ||
d.SetId(firewall.Name) | ||
|
||
err = computeOperationWait(config, op, project, "Creating Firewall") | ||
err = computeSharedOperationWait(config, op, project, "Creating Firewall") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return resourceComputeFirewallRead(d, meta) | ||
} | ||
|
||
func flattenAllowed(allowed []*compute.FirewallAllowed) []map[string]interface{} { | ||
func flattenAllowed(allowed []*computeBeta.FirewallAllowed) []map[string]interface{} { | ||
result := make([]map[string]interface{}, 0, len(allowed)) | ||
for _, allow := range allowed { | ||
allowMap := make(map[string]interface{}) | ||
|
@@ -159,22 +212,53 @@ func flattenAllowed(allowed []*compute.FirewallAllowed) []map[string]interface{} | |
return result | ||
} | ||
|
||
func flattenDenied(denied []*computeBeta.FirewallDenied) []map[string]interface{} { | ||
result := make([]map[string]interface{}, 0, len(denied)) | ||
for _, deny := range denied { | ||
denyMap := make(map[string]interface{}) | ||
denyMap["protocol"] = deny.IPProtocol | ||
denyMap["ports"] = deny.Ports | ||
|
||
result = append(result, denyMap) | ||
} | ||
return result | ||
} | ||
|
||
func resourceComputeFirewallRead(d *schema.ResourceData, meta interface{}) error { | ||
computeApiVersion := getComputeApiVersion(d, FirewallBaseApiVersion, FirewallVersionedFeatures) | ||
config := meta.(*Config) | ||
|
||
project, err := getProject(d, config) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
firewall, err := config.clientCompute.Firewalls.Get( | ||
project, d.Id()).Do() | ||
if err != nil { | ||
return handleNotFoundError(err, d, fmt.Sprintf("Firewall %q", d.Get("name").(string))) | ||
firewall := &computeBeta.Firewall{} | ||
switch computeApiVersion { | ||
case v1: | ||
firewallV1, err := config.clientCompute.Firewalls.Get(project, d.Id()).Do() | ||
if err != nil { | ||
return handleNotFoundError(err, d, fmt.Sprintf("Firewall %q", d.Get("name").(string))) | ||
} | ||
|
||
err = Convert(firewallV1, firewall) | ||
if err != nil { | ||
return err | ||
} | ||
case v0beta: | ||
firewallV0Beta, err := config.clientComputeBeta.Firewalls.Get(project, d.Id()).Do() | ||
if err != nil { | ||
return handleNotFoundError(err, d, fmt.Sprintf("Firewall %q", d.Get("name").(string))) | ||
} | ||
|
||
err = Convert(firewallV0Beta, firewall) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
networkUrl := strings.Split(firewall.Network, "/") | ||
d.Set("self_link", firewall.SelfLink) | ||
d.Set("self_link", ConvertSelfLinkToV1(firewall.SelfLink)) | ||
d.Set("name", firewall.Name) | ||
d.Set("network", networkUrl[len(networkUrl)-1]) | ||
d.Set("description", firewall.Description) | ||
|
@@ -183,10 +267,12 @@ func resourceComputeFirewallRead(d *schema.ResourceData, meta interface{}) error | |
d.Set("source_tags", firewall.SourceTags) | ||
d.Set("target_tags", firewall.TargetTags) | ||
d.Set("allow", flattenAllowed(firewall.Allowed)) | ||
d.Set("deny", flattenDenied(firewall.Denied)) | ||
return nil | ||
} | ||
|
||
func resourceComputeFirewallUpdate(d *schema.ResourceData, meta interface{}) error { | ||
computeApiVersion := getComputeApiVersionUpdate(d, FirewallBaseApiVersion, FirewallVersionedFeatures, []Feature{}) | ||
config := meta.(*Config) | ||
|
||
project, err := getProject(d, config) | ||
|
@@ -196,18 +282,38 @@ func resourceComputeFirewallUpdate(d *schema.ResourceData, meta interface{}) err | |
|
||
d.Partial(true) | ||
|
||
firewall, err := resourceFirewall(d, meta) | ||
firewall, err := resourceFirewall(d, meta, computeApiVersion) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
op, err := config.clientCompute.Firewalls.Update( | ||
project, d.Id(), firewall).Do() | ||
if err != nil { | ||
return fmt.Errorf("Error updating firewall: %s", err) | ||
var op interface{} | ||
switch computeApiVersion { | ||
case v1: | ||
firewallV1 := &compute.Firewall{} | ||
err := Convert(firewall, firewallV1) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
op, err = config.clientCompute.Firewalls.Update(project, d.Id(), firewallV1).Do() | ||
if err != nil { | ||
return fmt.Errorf("Error updating firewall: %s", err) | ||
} | ||
case v0beta: | ||
firewallV0Beta := &computeBeta.Firewall{} | ||
err := Convert(firewall, firewallV0Beta) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
op, err = config.clientComputeBeta.Firewalls.Update(project, d.Id(), firewallV0Beta).Do() | ||
if err != nil { | ||
return fmt.Errorf("Error updating firewall: %s", err) | ||
} | ||
} | ||
|
||
err = computeOperationWait(config, op, project, "Updating Firewall") | ||
err = computeSharedOperationWait(config, op, project, "Updating Firewall") | ||
if err != nil { | ||
return err | ||
} | ||
|
@@ -218,6 +324,7 @@ func resourceComputeFirewallUpdate(d *schema.ResourceData, meta interface{}) err | |
} | ||
|
||
func resourceComputeFirewallDelete(d *schema.ResourceData, meta interface{}) error { | ||
computeApiVersion := getComputeApiVersion(d, FirewallBaseApiVersion, FirewallVersionedFeatures) | ||
config := meta.(*Config) | ||
|
||
project, err := getProject(d, config) | ||
|
@@ -226,13 +333,21 @@ func resourceComputeFirewallDelete(d *schema.ResourceData, meta interface{}) err | |
} | ||
|
||
// Delete the firewall | ||
op, err := config.clientCompute.Firewalls.Delete( | ||
project, d.Id()).Do() | ||
if err != nil { | ||
return fmt.Errorf("Error deleting firewall: %s", err) | ||
var op interface{} | ||
switch computeApiVersion { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm I wonder- do deletes actually need to be done with a particular version of the api? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm honestly not sure; for now, for completeness for now, I'd like to perform all operations at the resource's API version; we'll be able to bring deletes of versioned resources either down to |
||
case v1: | ||
op, err = config.clientCompute.Firewalls.Delete(project, d.Id()).Do() | ||
if err != nil { | ||
return fmt.Errorf("Error deleting firewall: %s", err) | ||
} | ||
case v0beta: | ||
op, err = config.clientComputeBeta.Firewalls.Delete(project, d.Id()).Do() | ||
if err != nil { | ||
return fmt.Errorf("Error deleting firewall: %s", err) | ||
} | ||
} | ||
|
||
err = computeOperationWait(config, op, project, "Deleting Firewall") | ||
err = computeSharedOperationWait(config, op, project, "Deleting Firewall") | ||
if err != nil { | ||
return err | ||
} | ||
|
@@ -241,24 +356,41 @@ func resourceComputeFirewallDelete(d *schema.ResourceData, meta interface{}) err | |
return nil | ||
} | ||
|
||
func resourceFirewall( | ||
d *schema.ResourceData, | ||
meta interface{}) (*compute.Firewall, error) { | ||
func resourceFirewall(d *schema.ResourceData, meta interface{}, computeApiVersion ComputeApiVersion) (*computeBeta.Firewall, error) { | ||
config := meta.(*Config) | ||
|
||
project, _ := getProject(d, config) | ||
|
||
// Look up the network to attach the firewall to | ||
network, err := config.clientCompute.Networks.Get( | ||
project, d.Get("network").(string)).Do() | ||
network, err := config.clientCompute.Networks.Get(project, d.Get("network").(string)).Do() | ||
if err != nil { | ||
return nil, fmt.Errorf("Error reading network: %s", err) | ||
} | ||
|
||
// Build up the list of allowed entries | ||
var allowed []*compute.FirewallAllowed | ||
var allowed []*computeBeta.FirewallAllowed | ||
if v := d.Get("allow").(*schema.Set); v.Len() > 0 { | ||
allowed = make([]*compute.FirewallAllowed, 0, v.Len()) | ||
allowed = make([]*computeBeta.FirewallAllowed, 0, v.Len()) | ||
for _, v := range v.List() { | ||
m := v.(map[string]interface{}) | ||
|
||
var ports []string | ||
if v := convertStringArr(m["ports"].([]interface{})); len(v) > 0 { | ||
ports = make([]string, len(v)) | ||
for i, v := range v { | ||
ports[i] = v | ||
} | ||
} | ||
|
||
allowed = append(allowed, &computeBeta.FirewallAllowed{ | ||
IPProtocol: m["protocol"].(string), | ||
Ports: ports, | ||
}) | ||
} | ||
} | ||
|
||
// Build up the list of denied entries | ||
var denied []*computeBeta.FirewallDenied | ||
if v := d.Get("deny").(*schema.Set); v.Len() > 0 { | ||
denied = make([]*computeBeta.FirewallDenied, 0, v.Len()) | ||
for _, v := range v.List() { | ||
m := v.(map[string]interface{}) | ||
|
||
|
@@ -270,7 +402,7 @@ func resourceFirewall( | |
} | ||
} | ||
|
||
allowed = append(allowed, &compute.FirewallAllowed{ | ||
denied = append(denied, &computeBeta.FirewallDenied{ | ||
IPProtocol: m["protocol"].(string), | ||
Ports: ports, | ||
}) | ||
|
@@ -302,11 +434,12 @@ func resourceFirewall( | |
} | ||
|
||
// Build the firewall parameter | ||
return &compute.Firewall{ | ||
return &computeBeta.Firewall{ | ||
Name: d.Get("name").(string), | ||
Description: d.Get("description").(string), | ||
Network: network.SelfLink, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this self link need to be converted to v1? Also, do we actually need to read the network at both versions or can we just read it at v1 since all we need is the self link? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This variable never gets stored in state, since we do that in We can just read at |
||
Allowed: allowed, | ||
Denied: denied, | ||
SourceRanges: sourceRanges, | ||
SourceTags: sourceTags, | ||
TargetTags: targetTags, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't have to do this in this PR, but I'd like to see the beta operations get simplified like the v1 ones did
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For sure - what I'd like to do is convert the
Operation
object tov1
and call thev1
waits, like in #187Otherwise, we can just copy
compute_operation.go
again.