Skip to content

Commit f5eb70a

Browse files
Update and Refresh for create_vnic_details (#301)
* Update and Refresh for create_vnic_details Schema TypeMap as is currently used for create_vnic_details does not support properties such as Computed and ForceNew, which prevented using this with update and refresh. TypeList does appear to handle these correctly, and switching does not require any changes from users. Tests run: make test run=ResourceCoreVnicAttachmentTestSuite debug=1 make test run=ResourceCoreInstanceTestSuite debug=1 * respond to comments
1 parent e143990 commit f5eb70a

File tree

6 files changed

+325
-126
lines changed

6 files changed

+325
-126
lines changed

docs/examples/compute/multi_vnic/multi_vnic.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ variable "region" {}
77
variable "ssh_public_key" {}
88

99
variable "SecondaryVnicCount" {
10-
default = 2
10+
default = 1
1111
}
1212

1313
# Choose an Availability Domain

helpers_core.go

Lines changed: 78 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,50 @@
33
package main
44

55
import (
6-
"strconv"
7-
6+
"github.com/hashicorp/terraform/helper/schema"
87
"github.com/oracle/bmcs-go-sdk"
98
)
109

11-
func SetCreateVnicOptions(rawCreateVnicDetails interface{}) (vnicOpts *baremetal.CreateVnicOptions, err error) {
12-
vnic := rawCreateVnicDetails.(map[string]interface{})
10+
var createVnicDetailsSchema = &schema.Resource{
11+
Schema: map[string]*schema.Schema{
12+
"assign_public_ip": {
13+
Type: schema.TypeBool,
14+
Optional: true,
15+
Default: true,
16+
ForceNew: true,
17+
},
18+
"display_name": {
19+
Type: schema.TypeString,
20+
Optional: true,
21+
Computed: true,
22+
},
23+
"hostname_label": {
24+
Type: schema.TypeString,
25+
Optional: true,
26+
Computed: true,
27+
},
28+
"private_ip": {
29+
Type: schema.TypeString,
30+
Optional: true,
31+
Computed: true,
32+
ForceNew: true,
33+
},
34+
"skip_source_dest_check": {
35+
Type: schema.TypeBool,
36+
Optional: true,
37+
Default: false,
38+
},
39+
"subnet_id": {
40+
Type: schema.TypeString,
41+
Required: true,
42+
ForceNew: true,
43+
},
44+
},
45+
}
46+
47+
// vnicDetailsList is assumed to be non-nil and non-empty.
48+
func SetCreateVnicOptions(vnicDetailsList []interface{}) (vnicOpts *baremetal.CreateVnicOptions) {
49+
vnic := vnicDetailsList[0].(map[string]interface{})
1350

1451
vnicOpts = &baremetal.CreateVnicOptions{}
1552
vnicOpts.SubnetID = vnic["subnet_id"].(string)
@@ -29,21 +66,52 @@ func SetCreateVnicOptions(rawCreateVnicDetails interface{}) (vnicOpts *baremetal
2966
vnicOpts.PrivateIp = privateIp.(string)
3067
}
3168

32-
// Work around for tf bug https://github.com/hashicorp/terraform/issues/13512.
33-
// For bool values that are nested in maps, if the value is set to true/false then
34-
// it will appear here as "1"/"0". However, if the value is set to "true"/"false"
35-
// then it will appear here as "true"/"false". ParseBool() handles both of these cases.
3669
assignPublicIp := vnic["assign_public_ip"]
3770
if assignPublicIp != nil {
3871
vnicOpts.AssignPublicIp = new(bool)
39-
*vnicOpts.AssignPublicIp, err = strconv.ParseBool(assignPublicIp.(string))
72+
*vnicOpts.AssignPublicIp = assignPublicIp.(bool)
73+
}
74+
75+
skipSourceDestCheck := vnic["skip_source_dest_check"]
76+
if skipSourceDestCheck != nil {
77+
vnicOpts.SkipSourceDestCheck = new(bool)
78+
*vnicOpts.SkipSourceDestCheck = skipSourceDestCheck.(bool)
79+
}
80+
81+
return
82+
}
83+
84+
// vnicDetailsList is assumed to be non-nil and non-empty.
85+
func SetUpdateVnicOptions(vnicDetailsList []interface{}) (vnicOpts *baremetal.UpdateVnicOptions) {
86+
vnic := vnicDetailsList[0].(map[string]interface{})
87+
vnicOpts = &baremetal.UpdateVnicOptions{}
88+
89+
displayName := vnic["display_name"]
90+
if displayName != nil {
91+
vnicOpts.DisplayName = displayName.(string)
92+
}
93+
94+
hostnameLabel := vnic["hostname_label"]
95+
if hostnameLabel != nil {
96+
vnicOpts.HostnameLabel = hostnameLabel.(string)
4097
}
4198

4299
skipSourceDestCheck := vnic["skip_source_dest_check"]
43100
if skipSourceDestCheck != nil {
44101
vnicOpts.SkipSourceDestCheck = new(bool)
45-
*vnicOpts.SkipSourceDestCheck, err = strconv.ParseBool(skipSourceDestCheck.(string))
102+
*vnicOpts.SkipSourceDestCheck = skipSourceDestCheck.(bool)
46103
}
47104

48105
return
49106
}
107+
108+
func RefreshCreateVnicDetails(resourceData *schema.ResourceData, vnic *baremetal.Vnic) {
109+
vnicDetails := make(map[string]interface{})
110+
vnicDetails["subnet_id"] = vnic.SubnetID
111+
vnicDetails["assign_public_ip"] = len(vnic.PublicIPAddress) > 0
112+
vnicDetails["display_name"] = vnic.DisplayName
113+
vnicDetails["hostname_label"] = vnic.HostnameLabel
114+
vnicDetails["private_ip"] = vnic.PrivateIPAddress
115+
vnicDetails["skip_source_dest_check"] = vnic.SkipSourceDestCheck
116+
resourceData.Set("create_vnic_details", []interface{}{vnicDetails})
117+
}

resource_obmcs_core_instance.go

Lines changed: 50 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -30,43 +30,14 @@ func InstanceResource() *schema.Resource {
3030
Delete: deleteInstance,
3131
Schema: map[string]*schema.Schema{
3232
"create_vnic_details": {
33-
Type: schema.TypeMap,
33+
Type: schema.TypeList,
3434
Optional: true,
35-
ForceNew: true,
36-
Elem: &schema.Resource{
37-
Schema: map[string]*schema.Schema{
38-
"assign_public_ip": {
39-
Type: schema.TypeBool,
40-
Optional: true,
41-
ForceNew: true,
42-
},
43-
"display_name": {
44-
Type: schema.TypeString,
45-
Optional: true,
46-
ForceNew: true,
47-
},
48-
"hostname_label": {
49-
Type: schema.TypeString,
50-
Optional: true,
51-
ForceNew: true,
52-
},
53-
"private_ip": {
54-
Type: schema.TypeString,
55-
Optional: true,
56-
ForceNew: true,
57-
},
58-
"skip_source_dest_check": {
59-
Type: schema.TypeBool,
60-
Optional: true,
61-
ForceNew: true,
62-
},
63-
"subnet_id": {
64-
Type: schema.TypeString,
65-
Required: true,
66-
ForceNew: true,
67-
},
68-
},
69-
},
35+
// This must be set to computed, since it's optional and required subnet_id param is being refreshed.
36+
// If this isn't computed, then that would always force a change on users who do not set create_vnic_details.
37+
Computed: true,
38+
MaxItems: 1,
39+
MinItems: 1,
40+
Elem: createVnicDetailsSchema,
7041
},
7142
"availability_domain": {
7243
Type: schema.TypeString,
@@ -260,7 +231,7 @@ func (s *InstanceResourceCrud) Create() (e error) {
260231
}
261232

262233
if rawVnic, ok := s.D.GetOk("create_vnic_details"); ok {
263-
opts.CreateVnicOptions, e = SetCreateVnicOptions(rawVnic)
234+
opts.CreateVnicOptions = SetCreateVnicOptions(rawVnic.([]interface{}))
264235
}
265236

266237
if e == nil {
@@ -277,12 +248,11 @@ func (s *InstanceResourceCrud) Create() (e error) {
277248
}
278249

279250
/*
280-
* Return the public, private IP pair associated with the instance's primary Vnic.
251+
* Return the primary VNIC for this instance.
281252
*
282-
* NOTE while the instance is still being created, calls to this function
283-
* can return an error priort to the Vnic being attached.
253+
* Note that this may return an error during instance creation or deletion.
284254
*/
285-
func (s *InstanceResourceCrud) getInstanceIPs() (public_ip string, private_ip string, e error) {
255+
func (s *InstanceResourceCrud) getPrimaryVnic() (vnic *baremetal.Vnic, e error) {
286256
compartmentID := s.Resource.CompartmentID
287257

288258
opts := &baremetal.ListVnicAttachmentsOptions{}
@@ -303,7 +273,7 @@ func (s *InstanceResourceCrud) getInstanceIPs() (public_ip string, private_ip st
303273
}
304274

305275
if len(attachments) < 1 {
306-
return "", "", errors.New("No VNIC attachments found.")
276+
return nil, errors.New("No VNIC attachments found.")
307277
}
308278

309279
for _, attachment := range attachments {
@@ -312,12 +282,12 @@ func (s *InstanceResourceCrud) getInstanceIPs() (public_ip string, private_ip st
312282

313283
// Ignore errors on GetVnic, since we might not have permissions to view some secondary VNICs.
314284
if vnic != nil && vnic.IsPrimary {
315-
return vnic.PublicIPAddress, vnic.PrivateIPAddress, nil
285+
return vnic, nil
316286
}
317287
}
318288
}
319289

320-
return "", "", errors.New("Primary VNIC not found.")
290+
return nil, errors.New("Primary VNIC not found.")
321291
}
322292

323293
func (s *InstanceResourceCrud) Get() (e error) {
@@ -326,25 +296,6 @@ func (s *InstanceResourceCrud) Get() (e error) {
326296
s.Resource = res
327297
}
328298

329-
if e != nil {
330-
return e
331-
}
332-
333-
// Compute instance IPs through attached Vnic
334-
if s.Resource.State != baremetal.ResourceRunning {
335-
return
336-
}
337-
public_ip, private_ip, e2 := s.getInstanceIPs()
338-
if e2 != nil {
339-
log.Printf("[DEBUG] Primary VNIC could not be found: %q (InstanceID: %q, State: %q)", e2, s.Resource.ID, s.Resource.State)
340-
}
341-
342-
if public_ip != "" {
343-
s.public_ip = public_ip
344-
}
345-
if private_ip != "" {
346-
s.private_ip = private_ip
347-
}
348299
return
349300
}
350301

@@ -355,6 +306,27 @@ func (s *InstanceResourceCrud) Update() (e error) {
355306
}
356307

357308
s.Resource, e = s.Client.UpdateInstance(s.D.Id(), opts)
309+
if e != nil {
310+
return
311+
}
312+
313+
// HasChange returns true for any changes within create_vnic_details.
314+
if !s.D.HasChange("create_vnic_details") {
315+
log.Printf("[DEBUG] No changes to primary VNIC. Instance ID: %q", s.Resource.ID)
316+
return
317+
}
318+
319+
log.Printf("[DEBUG] Updating instance's primary VNIC. Instance ID: %q", s.Resource.ID)
320+
vnic, e := s.getPrimaryVnic()
321+
if e != nil {
322+
log.Printf("[ERROR] Primary VNIC could not be found during instance update: %q (Instance ID: %q, State: %q)", e, s.Resource.ID, s.Resource.State)
323+
return
324+
}
325+
326+
if rawVnic, ok := s.D.GetOk("create_vnic_details"); ok {
327+
_, e = s.Client.UpdateVnic(vnic.ID, SetUpdateVnicOptions(rawVnic.([]interface{})))
328+
}
329+
358330
return
359331
}
360332

@@ -369,8 +341,21 @@ func (s *InstanceResourceCrud) SetData() {
369341
s.D.Set("shape", s.Resource.Shape)
370342
s.D.Set("state", s.Resource.State)
371343
s.D.Set("time_created", s.Resource.TimeCreated.String())
372-
s.D.Set("public_ip", s.public_ip)
373-
s.D.Set("private_ip", s.private_ip)
344+
345+
if s.Resource.State != baremetal.ResourceRunning {
346+
return
347+
}
348+
349+
vnic, vnicError := s.getPrimaryVnic()
350+
if vnicError != nil {
351+
log.Printf("[WARN] Primary VNIC could not be found during instance refresh: %q (Instance ID: %q, State: %q)", vnicError, s.Resource.ID, s.Resource.State)
352+
return
353+
}
354+
355+
s.D.Set("public_ip", vnic.PublicIPAddress)
356+
s.D.Set("private_ip", vnic.PrivateIPAddress)
357+
358+
RefreshCreateVnicDetails(s.D, vnic)
374359
}
375360

376361
func (s *InstanceResourceCrud) Delete() (e error) {

0 commit comments

Comments
 (0)