Skip to content
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

Merged
merged 6 commits into from
Aug 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions google/compute_shared_operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,25 @@ import (
"google.golang.org/api/compute/v1"
)

func computeSharedOperationWait(config *Config, op interface{}, project string, activity string) error {
Copy link
Contributor

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

Copy link
Collaborator Author

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 to v1 and call the v1 waits, like in #187

Otherwise, we can just copy compute_operation.go again.

return computeSharedOperationWaitTime(config, op, project, 4, activity)
}

func computeSharedOperationWaitTime(config *Config, op interface{}, project string, minutes int, activity string) error {
if op == nil {
panic("Attempted to wait on an Operation that was nil.")
}

switch op.(type) {
case *compute.Operation:
return computeOperationWaitTime(config, op.(*compute.Operation), project, activity, minutes)
case *computeBeta.Operation:
return computeBetaOperationWaitGlobalTime(config, op.(*computeBeta.Operation), project, activity, minutes)
default:
panic("Attempted to wait on an Operation of unknown type.")
}
}

func computeSharedOperationWaitZone(config *Config, op interface{}, project string, zone, activity string) error {
return computeSharedOperationWaitZoneTime(config, op, project, zone, 4, activity)
}
Expand Down
209 changes: 171 additions & 38 deletions google/resource_compute_firewall.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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": {
Expand All @@ -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": {
Expand Down Expand Up @@ -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)))
Expand All @@ -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{})
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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
}
Expand All @@ -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)
Expand All @@ -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 {
Copy link
Contributor

Choose a reason for hiding this comment

The 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?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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 v1 or up to v0beta every time later, unless we adopt a multiversioned service generated by #240 which makes this mostly a non-issue.

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
}
Expand All @@ -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{})

Expand All @@ -270,7 +402,7 @@ func resourceFirewall(
}
}

allowed = append(allowed, &compute.FirewallAllowed{
denied = append(denied, &computeBeta.FirewallDenied{
IPProtocol: m["protocol"].(string),
Ports: ports,
})
Expand Down Expand Up @@ -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,
Copy link
Contributor

Choose a reason for hiding this comment

The 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?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variable never gets stored in state, since we do that in Read.

We can just read at v1; done.

Allowed: allowed,
Denied: denied,
SourceRanges: sourceRanges,
SourceTags: sourceTags,
TargetTags: targetTags,
Expand Down
Loading