From df2a192e4a65791e81c36efe92df2f066032df6c Mon Sep 17 00:00:00 2001 From: Brian Hicks Date: Tue, 29 Mar 2016 17:13:03 -0500 Subject: [PATCH] triton: add fabric resource --- builtin/providers/triton/provider.go | 1 + builtin/providers/triton/resource_fabric.go | 178 ++++++++++++++++++ .../providers/triton/resource_fabric_test.go | 104 ++++++++++ 3 files changed, 283 insertions(+) create mode 100644 builtin/providers/triton/resource_fabric.go create mode 100644 builtin/providers/triton/resource_fabric_test.go diff --git a/builtin/providers/triton/provider.go b/builtin/providers/triton/provider.go index c13681431..216ed1c08 100644 --- a/builtin/providers/triton/provider.go +++ b/builtin/providers/triton/provider.go @@ -47,6 +47,7 @@ func Provider() terraform.ResourceProvider { "triton_machine": resourceMachine(), "triton_key": resourceKey(), "triton_vlan": resourceVLAN(), + "triton_fabric": resourceFabric(), }, ConfigureFunc: providerConfigure, } diff --git a/builtin/providers/triton/resource_fabric.go b/builtin/providers/triton/resource_fabric.go new file mode 100644 index 000000000..be85a4381 --- /dev/null +++ b/builtin/providers/triton/resource_fabric.go @@ -0,0 +1,178 @@ +package triton + +import ( + "fmt" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/joyent/gosdc/cloudapi" +) + +func resourceFabric() *schema.Resource { + return &schema.Resource{ + Create: resourceFabricCreate, + Exists: resourceFabricExists, + Read: resourceFabricRead, + Delete: resourceFabricDelete, + + Schema: map[string]*schema.Schema{ + "name": { + Description: "network name", + Required: true, + ForceNew: true, + Type: schema.TypeString, + }, + "public": { + Description: "whether or not this is an RFC1918 network", + Computed: true, + Type: schema.TypeBool, + }, + "fabric": { + Description: "whether or not this network is on a fabric", + Computed: true, + Type: schema.TypeBool, + }, + "description": { + Description: "optional description of network", + Optional: true, + ForceNew: true, + Type: schema.TypeString, + }, + "subnet": { + Description: "CIDR formatted string describing network", + Required: true, + ForceNew: true, + Type: schema.TypeString, + }, + "provision_start_ip": { + Description: "first IP on the network that can be assigned", + Required: true, + ForceNew: true, + Type: schema.TypeString, + }, + "provision_end_ip": { + Description: "last assignable IP on the network", + Required: true, + ForceNew: true, + Type: schema.TypeString, + }, + "gateway": { + Description: "optional gateway IP", + Optional: true, + ForceNew: true, + Type: schema.TypeString, + }, + "resolvers": { + Description: "array of IP addresses for resolvers", + Optional: true, + Computed: true, + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "routes": { + Description: "map of CIDR block to Gateway IP address", + Computed: true, + Optional: true, + ForceNew: true, + Type: schema.TypeMap, + }, + "internet_nat": { + Description: "if a NAT zone is provisioned at Gateway IP address", + Computed: true, + Optional: true, + ForceNew: true, + Type: schema.TypeBool, + }, + "vlan_id": { + Description: "VLAN network is on", + Required: true, + ForceNew: true, + Type: schema.TypeInt, + }, + }, + } +} + +func resourceFabricCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*cloudapi.Client) + + var resolvers []string + for _, resolver := range d.Get("resolvers").([]interface{}) { + resolvers = append(resolvers, resolver.(string)) + } + + routes := map[string]string{} + for cidr, v := range d.Get("routes").(map[string]interface{}) { + ip, ok := v.(string) + if !ok { + return fmt.Errorf(`cannot use "%v" as an IP address`, v) + } + routes[cidr] = ip + } + + fabric, err := client.CreateFabricNetwork( + int16(d.Get("vlan_id").(int)), + cloudapi.CreateFabricNetworkOpts{ + Name: d.Get("name").(string), + Description: d.Get("description").(string), + Subnet: d.Get("subnet").(string), + ProvisionStartIp: d.Get("provision_start_ip").(string), + ProvisionEndIp: d.Get("provision_end_ip").(string), + Gateway: d.Get("gateway").(string), + Resolvers: resolvers, + Routes: routes, + InternetNAT: d.Get("internet_nat").(bool), + }, + ) + if err != nil { + return err + } + + d.SetId(fabric.Id) + + err = resourceFabricRead(d, meta) + if err != nil { + return err + } + + return nil +} + +func resourceFabricExists(d *schema.ResourceData, meta interface{}) (bool, error) { + client := meta.(*cloudapi.Client) + + fabric, err := client.GetFabricNetwork(int16(d.Get("vlan_id").(int)), d.Id()) + + return fabric != nil && err == nil, err +} + +func resourceFabricRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*cloudapi.Client) + + fabric, err := client.GetFabricNetwork(int16(d.Get("vlan_id").(int)), d.Id()) + if err != nil { + return err + } + + d.SetId(fabric.Id) + d.Set("name", fabric.Name) + d.Set("public", fabric.Public) + d.Set("public", fabric.Public) + d.Set("fabric", fabric.Fabric) + d.Set("description", fabric.Description) + d.Set("subnet", fabric.Subnet) + d.Set("provision_start_ip", fabric.ProvisionStartIp) + d.Set("provision_end_ip", fabric.ProvisionEndIp) + d.Set("gateway", fabric.Gateway) + d.Set("resolvers", fabric.Resolvers) + d.Set("routes", fabric.Routes) + d.Set("internet_nat", fabric.InternetNAT) + d.Set("vlan_id", fabric.VLANId) + + return nil +} + +func resourceFabricDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*cloudapi.Client) + + return client.DeleteFabricNetwork(int16(d.Get("vlan_id").(int)), d.Id()) +} diff --git a/builtin/providers/triton/resource_fabric_test.go b/builtin/providers/triton/resource_fabric_test.go new file mode 100644 index 000000000..a6b1d8847 --- /dev/null +++ b/builtin/providers/triton/resource_fabric_test.go @@ -0,0 +1,104 @@ +package triton + +import ( + "fmt" + "strconv" + "testing" + "time" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/joyent/gosdc/cloudapi" +) + +func TestAccTritonFabric_basic(t *testing.T) { + fabricName := fmt.Sprintf("acctest-%d", acctest.RandInt()) + config := fmt.Sprintf(testAccTritonFabric_basic, fabricName) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckTritonFabricDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckTritonFabricExists("triton_fabric.test"), + func(*terraform.State) error { + time.Sleep(10 * time.Second) + return nil + }, + ), + }, + }, + }) +} + +func testCheckTritonFabricExists(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 := strconv.ParseInt(rs.Primary.Attributes["vlan_id"], 10, 16) + if err != nil { + return err + } + + fabric, err := conn.GetFabricNetwork(int16(id), rs.Primary.ID) + if err != nil { + return fmt.Errorf("Bad: Check Fabric Exists: %s", err) + } + + if fabric == nil { + return fmt.Errorf("Bad: Fabric %q does not exist", rs.Primary.ID) + } + + return nil + } +} + +func testCheckTritonFabricDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*cloudapi.Client) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "triton_fabric" { + continue + } + + id, err := strconv.ParseInt(rs.Primary.Attributes["vlan_id"], 10, 16) + if err != nil { + return err + } + + fabric, err := conn.GetFabricNetwork(int16(id), rs.Primary.ID) + if err != nil { + return nil + } + + if fabric != nil { + return fmt.Errorf("Bad: Fabric %q still exists", rs.Primary.ID) + } + } + + return nil +} + +var testAccTritonFabric_basic = ` +resource "triton_fabric" "test" { + name = "%s" + description = "test network" + vlan_id = 2 # every DC seems to have a vlan 2 available + + subnet = "10.0.0.0/22" + gateway = "10.0.0.1" + provision_start_ip = "10.0.0.5" + provision_end_ip = "10.0.3.250" + + resolvers = ["8.8.8.8", "8.8.4.4"] +} +`