mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-07-05 17:52:38 +00:00
Add Beta support & Beta feature deny to google_compute_firewall (#282)
* Add versioned Beta support to google_compute_firewall. * Add Beta support for deny to google_compute_firewall. * remove extra line: * make fmt * Add missing ForceNew fields. * Respond to review comments testing functionality + reducing network GET to v1
This commit is contained in:
parent
df30b04b18
commit
2d0d8bdcc0
|
@ -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,6 +150,7 @@ 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)
|
||||
|
@ -125,21 +158,41 @@ func resourceComputeFirewallCreate(d *schema.ResourceData, meta interface{}) err
|
|||
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
|
||||
}
|
||||
|
@ -147,7 +200,7 @@ func resourceComputeFirewallCreate(d *schema.ResourceData, meta interface{}) 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,7 +212,20 @@ 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)
|
||||
|
@ -167,14 +233,32 @@ func resourceComputeFirewallRead(d *schema.ResourceData, meta interface{}) error
|
|||
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 {
|
||||
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,19 @@ 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{})
|
||||
|
||||
|
@ -270,7 +380,29 @@ func resourceFirewall(
|
|||
}
|
||||
}
|
||||
|
||||
allowed = append(allowed, &compute.FirewallAllowed{
|
||||
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{})
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
Allowed: allowed,
|
||||
Denied: denied,
|
||||
SourceRanges: sourceRanges,
|
||||
SourceTags: sourceTags,
|
||||
TargetTags: targetTags,
|
||||
|
|
|
@ -7,6 +7,8 @@ import (
|
|||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
|
||||
computeBeta "google.golang.org/api/compute/v0.beta"
|
||||
"google.golang.org/api/compute/v1"
|
||||
)
|
||||
|
||||
|
@ -82,6 +84,27 @@ func TestAccComputeFirewall_noSource(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAccComputeFirewall_denied(t *testing.T) {
|
||||
var firewall computeBeta.Firewall
|
||||
networkName := fmt.Sprintf("firewall-test-%s", acctest.RandString(10))
|
||||
firewallName := fmt.Sprintf("firewall-test-%s", acctest.RandString(10))
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckComputeFirewallDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccComputeFirewall_denied(networkName, firewallName),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckComputeBetaFirewallExists("google_compute_firewall.foobar", &firewall),
|
||||
testAccCheckComputeBetaFirewallDenyPorts(&firewall, "22"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckComputeFirewallDestroy(s *terraform.State) error {
|
||||
config := testAccProvider.Meta().(*Config)
|
||||
|
||||
|
@ -129,6 +152,35 @@ func testAccCheckComputeFirewallExists(n string, firewall *compute.Firewall) res
|
|||
}
|
||||
}
|
||||
|
||||
func testAccCheckComputeBetaFirewallExists(n string, firewall *computeBeta.Firewall) 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 ID is set")
|
||||
}
|
||||
|
||||
config := testAccProvider.Meta().(*Config)
|
||||
|
||||
found, err := config.clientComputeBeta.Firewalls.Get(
|
||||
config.Project, rs.Primary.ID).Do()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if found.Name != rs.Primary.ID {
|
||||
return fmt.Errorf("Firewall not found")
|
||||
}
|
||||
|
||||
*firewall = *found
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckComputeFirewallPorts(
|
||||
firewall *compute.Firewall, ports string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
|
@ -144,6 +196,20 @@ func testAccCheckComputeFirewallPorts(
|
|||
}
|
||||
}
|
||||
|
||||
func testAccCheckComputeBetaFirewallDenyPorts(firewall *computeBeta.Firewall, ports string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
if len(firewall.Denied) == 0 {
|
||||
return fmt.Errorf("no denied rules")
|
||||
}
|
||||
|
||||
if firewall.Denied[0].Ports[0] != ports {
|
||||
return fmt.Errorf("bad: %#v", firewall.Denied[0].Ports)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccComputeFirewall_basic(network, firewall string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "google_compute_network" "foobar" {
|
||||
|
@ -201,3 +267,23 @@ func testAccComputeFirewall_noSource(network, firewall string) string {
|
|||
}
|
||||
}`, network, firewall)
|
||||
}
|
||||
|
||||
func testAccComputeFirewall_denied(network, firewall string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "google_compute_network" "foobar" {
|
||||
name = "%s"
|
||||
ipv4_range = "10.0.0.0/16"
|
||||
}
|
||||
|
||||
resource "google_compute_firewall" "foobar" {
|
||||
name = "firewall-test-%s"
|
||||
description = "Resource created for Terraform acceptance testing"
|
||||
network = "${google_compute_network.foobar.name}"
|
||||
source_tags = ["foo"]
|
||||
|
||||
deny {
|
||||
protocol = "tcp"
|
||||
ports = [22]
|
||||
}
|
||||
}`, network, firewall)
|
||||
}
|
||||
|
|
|
@ -59,10 +59,23 @@ The following arguments are supported:
|
|||
|
||||
* `target_tags` - (Optional) A list of target tags for this firewall.
|
||||
|
||||
- - -
|
||||
|
||||
* `deny` - (Optional, Beta) Can be specified multiple times for each deny
|
||||
rule. Each deny block supports fields documented below. Can be specified
|
||||
instead of allow.
|
||||
|
||||
The `allow` block supports:
|
||||
|
||||
* `protocol` - (Required) The name of the protocol to allow.
|
||||
|
||||
* `ports` - (Optional) List of ports and/or port ranges to allow. This can
|
||||
only be specified if the protocol is TCP or UDP.
|
||||
|
||||
The `deny` block supports:
|
||||
|
||||
* `protocol` - (Required) The name of the protocol to allow.
|
||||
|
||||
* `ports` - (Optional) List of ports and/or port ranges to allow. This can
|
||||
only be specified if the protocol is TCP or UDP.
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user