Skip to content

Commit 43837fd

Browse files
committed
Merge pull request hashicorp#6418 from asteris-llc/f-triton-nic
provider/triton: Add NICs to triton_machine resources
2 parents 0f3237a + a4eb845 commit 43837fd

2 files changed

Lines changed: 295 additions & 13 deletions

File tree

builtin/providers/triton/resource_machine.go

Lines changed: 127 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"regexp"
77
"time"
88

9+
"github.com/hashicorp/terraform/helper/hashcode"
910
"github.com/hashicorp/terraform/helper/schema"
1011
"github.com/joyent/gosdc/cloudapi"
1112
)
@@ -108,17 +109,53 @@ func resourceMachine() *schema.Resource {
108109
Type: schema.TypeString,
109110
Computed: true,
110111
},
111-
"networks": {
112-
Description: "desired network IDs",
113-
Type: schema.TypeList,
114-
Optional: true,
112+
"nic": {
113+
Description: "network interface",
114+
Type: schema.TypeSet,
115115
Computed: true,
116-
// TODO: this really should ForceNew but the Network IDs don't seem to
117-
// be returned by the API, meaning if we track them here TF will replace
118-
// the resource on every run.
119-
// ForceNew: true,
120-
Elem: &schema.Schema{
121-
Type: schema.TypeString,
116+
Optional: true,
117+
Set: func(v interface{}) int {
118+
m := v.(map[string]interface{})
119+
return hashcode.String(m["network"].(string))
120+
},
121+
Elem: &schema.Resource{
122+
Schema: map[string]*schema.Schema{
123+
"ip": {
124+
Description: "NIC's IPv4 address",
125+
Computed: true,
126+
Type: schema.TypeString,
127+
},
128+
"mac": {
129+
Description: "NIC's MAC address",
130+
Computed: true,
131+
Type: schema.TypeString,
132+
},
133+
"primary": {
134+
Description: "Whether this is the machine's primary NIC",
135+
Computed: true,
136+
Type: schema.TypeBool,
137+
},
138+
"netmask": {
139+
Description: "IPv4 netmask",
140+
Computed: true,
141+
Type: schema.TypeString,
142+
},
143+
"gateway": {
144+
Description: "IPv4 gateway",
145+
Computed: true,
146+
Type: schema.TypeString,
147+
},
148+
"state": {
149+
Description: "describes the state of the NIC (e.g. provisioning, running, or stopped)",
150+
Computed: true,
151+
Type: schema.TypeString,
152+
},
153+
"network": {
154+
Description: "Network ID this NIC is attached to",
155+
Required: true,
156+
Type: schema.TypeString,
157+
},
158+
},
122159
},
123160
},
124161
"firewall_enabled": {
@@ -153,6 +190,18 @@ func resourceMachine() *schema.Resource {
153190
Optional: true,
154191
Computed: true,
155192
},
193+
194+
// deprecated fields
195+
"networks": {
196+
Description: "desired network IDs",
197+
Type: schema.TypeList,
198+
Optional: true,
199+
Computed: true,
200+
Deprecated: "Networks is deprecated, please use `nic`",
201+
Elem: &schema.Schema{
202+
Type: schema.TypeString,
203+
},
204+
},
156205
},
157206
}
158207
}
@@ -164,6 +213,11 @@ func resourceMachineCreate(d *schema.ResourceData, meta interface{}) error {
164213
for _, network := range d.Get("networks").([]interface{}) {
165214
networks = append(networks, network.(string))
166215
}
216+
nics := d.Get("nic").(*schema.Set)
217+
for _, nicI := range nics.List() {
218+
nic := nicI.(map[string]interface{})
219+
networks = append(networks, nic["network"].(string))
220+
}
167221

168222
metadata := map[string]string{}
169223
for schemaName, metadataKey := range resourceMachineMetadataKeys {
@@ -221,6 +275,11 @@ func resourceMachineRead(d *schema.ResourceData, meta interface{}) error {
221275
return err
222276
}
223277

278+
nics, err := client.ListNICs(d.Id())
279+
if err != nil {
280+
return err
281+
}
282+
224283
d.SetId(machine.Id)
225284
d.Set("name", machine.Name)
226285
d.Set("type", machine.Type)
@@ -235,9 +294,31 @@ func resourceMachineRead(d *schema.ResourceData, meta interface{}) error {
235294
d.Set("package", machine.Package)
236295
d.Set("image", machine.Image)
237296
d.Set("primaryip", machine.PrimaryIP)
238-
d.Set("networks", machine.Networks)
239297
d.Set("firewall_enabled", machine.FirewallEnabled)
240298

299+
// create and update NICs
300+
var (
301+
machineNICs []map[string]interface{}
302+
networks []string
303+
)
304+
for _, nic := range nics {
305+
machineNICs = append(
306+
machineNICs,
307+
map[string]interface{}{
308+
"ip": nic.IP,
309+
"mac": nic.MAC,
310+
"primary": nic.Primary,
311+
"netmask": nic.Netmask,
312+
"gateway": nic.Gateway,
313+
"state": nic.State,
314+
"network": nic.Network,
315+
},
316+
)
317+
networks = append(networks, nic.Network)
318+
}
319+
d.Set("nic", machineNICs)
320+
d.Set("networks", networks)
321+
241322
// computed attributes from metadata
242323
for schemaName, metadataKey := range resourceMachineMetadataKeys {
243324
d.Set(schemaName, machine.Metadata[metadataKey])
@@ -349,6 +430,41 @@ func resourceMachineUpdate(d *schema.ResourceData, meta interface{}) error {
349430
d.SetPartial("firewall_enabled")
350431
}
351432

433+
if d.HasChange("nic") {
434+
o, n := d.GetChange("nic")
435+
if o == nil {
436+
o = new(schema.Set)
437+
}
438+
if n == nil {
439+
n = new(schema.Set)
440+
}
441+
442+
oldNICs := o.(*schema.Set)
443+
newNICs := o.(*schema.Set)
444+
445+
// add new NICs that are not in old NICs
446+
for _, nicI := range newNICs.Difference(oldNICs).List() {
447+
nic := nicI.(map[string]interface{})
448+
fmt.Printf("adding %+v\n", nic)
449+
_, err := client.AddNIC(d.Id(), nic["network"].(string))
450+
if err != nil {
451+
return err
452+
}
453+
}
454+
455+
// remove old NICs that are not in new NICs
456+
for _, nicI := range oldNICs.Difference(newNICs).List() {
457+
nic := nicI.(map[string]interface{})
458+
fmt.Printf("removing %+v\n", nic)
459+
err := client.RemoveNIC(d.Id(), nic["mac"].(string))
460+
if err != nil {
461+
return err
462+
}
463+
}
464+
465+
d.SetPartial("nic")
466+
}
467+
352468
// metadata stuff
353469
metadata := map[string]string{}
354470
for schemaName, metadataKey := range resourceMachineMetadataKeys {

builtin/providers/triton/resource_machine_test.go

Lines changed: 168 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,62 @@ func TestAccTritonMachine_basic(t *testing.T) {
3434
})
3535
}
3636

37+
func TestAccTritonMachine_nic(t *testing.T) {
38+
machineName := fmt.Sprintf("acctest-%d", acctest.RandInt())
39+
config := fmt.Sprintf(testAccTritonMachine_withnic, machineName, machineName)
40+
41+
resource.Test(t, resource.TestCase{
42+
PreCheck: func() { testAccPreCheck(t) },
43+
Providers: testAccProviders,
44+
CheckDestroy: testCheckTritonMachineDestroy,
45+
Steps: []resource.TestStep{
46+
resource.TestStep{
47+
Config: config,
48+
Check: resource.ComposeTestCheckFunc(
49+
testCheckTritonMachineExists("triton_machine.test"),
50+
func(*terraform.State) error {
51+
time.Sleep(10 * time.Second)
52+
return nil
53+
},
54+
testCheckTritonMachineHasFabric("triton_machine.test", "triton_fabric.test"),
55+
),
56+
},
57+
},
58+
})
59+
}
60+
61+
func TestAccTritonMachine_addnic(t *testing.T) {
62+
machineName := fmt.Sprintf("acctest-%d", acctest.RandInt())
63+
without := fmt.Sprintf(testAccTritonMachine_withoutnic, machineName, machineName)
64+
with := fmt.Sprintf(testAccTritonMachine_withnic, machineName, machineName)
65+
66+
resource.Test(t, resource.TestCase{
67+
PreCheck: func() { testAccPreCheck(t) },
68+
Providers: testAccProviders,
69+
CheckDestroy: testCheckTritonMachineDestroy,
70+
Steps: []resource.TestStep{
71+
resource.TestStep{
72+
Config: without,
73+
Check: resource.ComposeTestCheckFunc(
74+
testCheckTritonMachineExists("triton_machine.test"),
75+
func(*terraform.State) error {
76+
time.Sleep(10 * time.Second)
77+
return nil
78+
},
79+
testCheckTritonMachineHasNoFabric("triton_machine.test", "triton_fabric.test"),
80+
),
81+
},
82+
resource.TestStep{
83+
Config: with,
84+
Check: resource.ComposeTestCheckFunc(
85+
testCheckTritonMachineExists("triton_machine.test"),
86+
testCheckTritonMachineHasFabric("triton_machine.test", "triton_fabric.test"),
87+
),
88+
},
89+
},
90+
})
91+
}
92+
3793
func testCheckTritonMachineExists(name string) resource.TestCheckFunc {
3894
return func(s *terraform.State) error {
3995
// Ensure we have enough information in state to look up in API
@@ -56,6 +112,64 @@ func testCheckTritonMachineExists(name string) resource.TestCheckFunc {
56112
}
57113
}
58114

115+
func testCheckTritonMachineHasFabric(name, fabricName string) resource.TestCheckFunc {
116+
return func(s *terraform.State) error {
117+
// Ensure we have enough information in state to look up in API
118+
machine, ok := s.RootModule().Resources[name]
119+
if !ok {
120+
return fmt.Errorf("Not found: %s", name)
121+
}
122+
123+
network, ok := s.RootModule().Resources[fabricName]
124+
if !ok {
125+
return fmt.Errorf("Not found: %s", fabricName)
126+
}
127+
conn := testAccProvider.Meta().(*cloudapi.Client)
128+
129+
nics, err := conn.ListNICs(machine.Primary.ID)
130+
if err != nil {
131+
return fmt.Errorf("Bad: Check NICs Exist: %s", err)
132+
}
133+
134+
for _, nic := range nics {
135+
if nic.Network == network.Primary.ID {
136+
return nil
137+
}
138+
}
139+
140+
return fmt.Errorf("Bad: Machine %q does not have Fabric %q", machine.Primary.ID, network.Primary.ID)
141+
}
142+
}
143+
144+
func testCheckTritonMachineHasNoFabric(name, fabricName string) resource.TestCheckFunc {
145+
return func(s *terraform.State) error {
146+
// Ensure we have enough information in state to look up in API
147+
machine, ok := s.RootModule().Resources[name]
148+
if !ok {
149+
return fmt.Errorf("Not found: %s", name)
150+
}
151+
152+
network, ok := s.RootModule().Resources[fabricName]
153+
if !ok {
154+
return fmt.Errorf("Not found: %s", fabricName)
155+
}
156+
conn := testAccProvider.Meta().(*cloudapi.Client)
157+
158+
nics, err := conn.ListNICs(machine.Primary.ID)
159+
if err != nil {
160+
return fmt.Errorf("Bad: Check NICs Exist: %s", err)
161+
}
162+
163+
for _, nic := range nics {
164+
if nic.Network == network.Primary.ID {
165+
return fmt.Errorf("Bad: Machine %q has Fabric %q", machine.Primary.ID, network.Primary.ID)
166+
}
167+
}
168+
169+
return nil
170+
}
171+
}
172+
59173
func testCheckTritonMachineDestroy(s *terraform.State) error {
60174
conn := testAccProvider.Meta().(*cloudapi.Client)
61175

@@ -199,7 +313,59 @@ resource "triton_machine" "test" {
199313
user_data = "hello"
200314
201315
tags = {
202-
test = "hello!"
203-
}
316+
test = "hello!"
317+
}
318+
}
319+
`
320+
321+
var testAccTritonMachine_withnic = `
322+
resource "triton_fabric" "test" {
323+
name = "%s-network"
324+
description = "test network"
325+
vlan_id = 2 # every DC seems to have a vlan 2 available
326+
327+
subnet = "10.0.0.0/22"
328+
gateway = "10.0.0.1"
329+
provision_start_ip = "10.0.0.5"
330+
provision_end_ip = "10.0.3.250"
331+
332+
resolvers = ["8.8.8.8", "8.8.4.4"]
333+
}
334+
335+
resource "triton_machine" "test" {
336+
name = "%s"
337+
package = "g3-standard-0.25-smartos"
338+
image = "842e6fa6-6e9b-11e5-8402-1b490459e334"
339+
340+
tags = {
341+
test = "hello!"
342+
}
343+
344+
nic { network = "${triton_fabric.test.id}" }
345+
}
346+
`
347+
348+
var testAccTritonMachine_withoutnic = `
349+
resource "triton_fabric" "test" {
350+
name = "%s-network"
351+
description = "test network"
352+
vlan_id = 2 # every DC seems to have a vlan 2 available
353+
354+
subnet = "10.0.0.0/22"
355+
gateway = "10.0.0.1"
356+
provision_start_ip = "10.0.0.5"
357+
provision_end_ip = "10.0.3.250"
358+
359+
resolvers = ["8.8.8.8", "8.8.4.4"]
360+
}
361+
362+
resource "triton_machine" "test" {
363+
name = "%s"
364+
package = "g3-standard-0.25-smartos"
365+
image = "842e6fa6-6e9b-11e5-8402-1b490459e334"
366+
367+
tags = {
368+
test = "hello!"
369+
}
204370
}
205371
`

0 commit comments

Comments
 (0)