From 7332b0ef9ef4c8b89bd58aa56c67ff5e54c84f1a Mon Sep 17 00:00:00 2001 From: Brian Hicks Date: Tue, 29 Mar 2016 16:38:34 -0500 Subject: [PATCH] triton: add VLAN resource --- builtin/providers/triton/provider.go | 1 + builtin/providers/triton/resource_vlan.go | 132 ++++++++++++++++++ .../providers/triton/resource_vlan_test.go | 127 +++++++++++++++++ 3 files changed, 260 insertions(+) create mode 100644 builtin/providers/triton/resource_vlan.go create mode 100644 builtin/providers/triton/resource_vlan_test.go diff --git a/builtin/providers/triton/provider.go b/builtin/providers/triton/provider.go index a121eb647..c13681431 100644 --- a/builtin/providers/triton/provider.go +++ b/builtin/providers/triton/provider.go @@ -46,6 +46,7 @@ func Provider() terraform.ResourceProvider { "triton_firewall_rule": resourceFirewallRule(), "triton_machine": resourceMachine(), "triton_key": resourceKey(), + "triton_vlan": resourceVLAN(), }, ConfigureFunc: providerConfigure, } diff --git a/builtin/providers/triton/resource_vlan.go b/builtin/providers/triton/resource_vlan.go new file mode 100644 index 000000000..ece7aeade --- /dev/null +++ b/builtin/providers/triton/resource_vlan.go @@ -0,0 +1,132 @@ +package triton + +import ( + "errors" + "strconv" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/joyent/gosdc/cloudapi" +) + +func resourceVLAN() *schema.Resource { + return &schema.Resource{ + Create: resourceVLANCreate, + Exists: resourceVLANExists, + Read: resourceVLANRead, + Update: resourceVLANUpdate, + Delete: resourceVLANDelete, + + Schema: map[string]*schema.Schema{ + "vlan_id": { + Description: "number between 2-4095 indicating VLAN ID", + Required: true, + ForceNew: true, + Type: schema.TypeInt, + ValidateFunc: func(val interface{}, field string) (warn []string, err []error) { + value := val.(int) + if value < 0 || value > 4095 { + err = append(err, errors.New("id must be between 2 and 4095")) + } + return + }, + }, + "name": { + Description: "Unique name to identify VLAN", + Required: true, + Type: schema.TypeString, + }, + "description": { + Description: "Optional description of the VLAN", + Optional: true, + Type: schema.TypeString, + }, + }, + } +} + +func resourceVLANCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*cloudapi.Client) + + vlan, err := client.CreateFabricVLAN(cloudapi.FabricVLAN{ + Id: int16(d.Get("vlan_id").(int)), + Name: d.Get("name").(string), + Description: d.Get("description").(string), + }) + if err != nil { + return err + } + + d.SetId(resourceVLANIDString(vlan.Id)) + return resourceVLANRead(d, meta) +} + +func resourceVLANExists(d *schema.ResourceData, meta interface{}) (bool, error) { + client := meta.(*cloudapi.Client) + + id, err := resourceVLANIDInt16(d.Id()) + if err != nil { + return false, err + } + + vlan, err := client.GetFabricVLAN(id) + + return vlan != nil && err == nil, err +} + +func resourceVLANRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*cloudapi.Client) + + vlan, err := client.GetFabricVLAN(int16(d.Get("vlan_id").(int))) + if err != nil { + return err + } + + d.SetId(resourceVLANIDString(vlan.Id)) + d.Set("vlan_id", vlan.Id) + d.Set("name", vlan.Name) + d.Set("description", vlan.Description) + + return nil +} + +func resourceVLANUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*cloudapi.Client) + + vlan, err := client.UpdateFabricVLAN(cloudapi.FabricVLAN{ + Id: int16(d.Get("vlan_id").(int)), + Name: d.Get("name").(string), + Description: d.Get("description").(string), + }) + if err != nil { + return err + } + + d.SetId(resourceVLANIDString(vlan.Id)) + return resourceVLANRead(d, meta) +} + +func resourceVLANDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*cloudapi.Client) + + id, err := resourceVLANIDInt16(d.Id()) + if err != nil { + return err + } + + return client.DeleteFabricVLAN(id) +} + +// convenience conversion functions + +func resourceVLANIDString(id int16) string { + return strconv.Itoa(int(id)) +} + +func resourceVLANIDInt16(id string) (int16, error) { + result, err := strconv.ParseInt(id, 10, 16) + if err != nil { + return 0, err + } + + return int16(result), nil +} diff --git a/builtin/providers/triton/resource_vlan_test.go b/builtin/providers/triton/resource_vlan_test.go new file mode 100644 index 000000000..91333aed6 --- /dev/null +++ b/builtin/providers/triton/resource_vlan_test.go @@ -0,0 +1,127 @@ +package triton + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/joyent/gosdc/cloudapi" +) + +func TestAccTritonVLAN_basic(t *testing.T) { + config := testAccTritonVLAN_basic + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckTritonVLANDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckTritonVLANExists("triton_vlan.test"), + ), + }, + }, + }) +} + +func TestAccTritonVLAN_update(t *testing.T) { + preConfig := testAccTritonVLAN_basic + postConfig := testAccTritonVLAN_update + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckTritonVLANDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckTritonVLANExists("triton_vlan.test"), + resource.TestCheckResourceAttr("triton_vlan.test", "name", "test-vlan"), + resource.TestCheckResourceAttr("triton_vlan.test", "description", "test vlan"), + ), + }, + + resource.TestStep{ + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckTritonVLANExists("triton_vlan.test"), + resource.TestCheckResourceAttr("triton_vlan.test", "name", "test-vlan-2"), + resource.TestCheckResourceAttr("triton_vlan.test", "description", "test vlan 2"), + ), + }, + }, + }) +} + +func testCheckTritonVLANExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + conn := testAccProvider.Meta().(*cloudapi.Client) + + id, err := resourceVLANIDInt16(rs.Primary.ID) + if err != nil { + return err + } + + rule, err := conn.GetFabricVLAN(id) + if err != nil { + return fmt.Errorf("Bad: Check VLAN Exists: %s", err) + } + + if rule == nil { + return fmt.Errorf("Bad: VLAN %q does not exist", rs.Primary.ID) + } + + return nil + } +} + +func testCheckTritonVLANDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*cloudapi.Client) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "triton_vlan" { + continue + } + + id, err := resourceVLANIDInt16(rs.Primary.ID) + if err != nil { + return err + } + + resp, err := conn.GetFabricVLAN(id) + if err != nil { + return nil + } + + if resp != nil { + return fmt.Errorf("Bad: VLAN %q still exists", rs.Primary.ID) + } + } + + return nil +} + +var testAccTritonVLAN_basic = ` +resource "triton_vlan" "test" { + vlan_id = 1024 + name = "test-vlan" + description = "test vlan" +} +` + +var testAccTritonVLAN_update = ` +resource "triton_vlan" "test" { + vlan_id = 1024 + name = "test-vlan-2" + description = "test vlan 2" +} +`