Skip to content

Commit 760b6e0

Browse files
author
Ravi Tandon
committed
Virtual Circuit public_prefixes to be updatable and importable
1 parent 2474922 commit 760b6e0

File tree

7 files changed

+184
-44
lines changed

7 files changed

+184
-44
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
### Fixed
44
- Timeout should be updatable for the `oci_containerengine_cluster` and `oci_containerengine_node_pool` resources
5+
- Virtual Circuit `public_prefixes` to be updatable and importable. [Issue #700](https://github.com/terraform-providers/terraform-provider-oci/issues/700)
56

67
## 3.14.0 (January 29, 2019)
78

GNUmakefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ GOFMT_FILES?=$$(find . -name '*.go' |grep -v vendor)
55
PKG_NAME=oci
66
WEBSITE_REPO=github.com/hashicorp/terraform-website
77

8-
prefix := $(if $(debug),TF_LOG=DEBUG OCI_GO_SDK_DEBUG=1, )
8+
prefix := $(if $(debug),TF_LOG=DEBUG OCI_GO_SDK_DEBUG=v, )
99
timeout := $(if $(timeout), $(timeout), 120m)
1010
run_regex := $(if $(run), -run $(run), )
1111
skip_goimports_check_flag := $(if $(skip_goimports_check), -s, )

docs/examples/fast_connect/variables.tf

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,19 @@ variable "virtual_circuit_display_name" {
4141
}
4242

4343
variable "virtual_circuit_public_prefixes_cidr_block" {
44-
default = "0.0.0.0/5"
44+
default = "0.0.0.0/6"
4545
}
4646

4747
variable "virtual_circuit_public_prefixes_cidr_block2" {
48-
default = "206.209.218.0/24"
48+
default = "206.209.218.0/25"
49+
}
50+
51+
variable "virtual_circuit_public_prefixes_cidr_block3" {
52+
default = "206.209.219.0/24"
4953
}
5054

5155
variable "virtual_circuit_region" {
52-
default = "r1"
56+
default = "us-phoenix-1"
5357
}
5458

5559
variable "virtual_circuit_state" {

docs/examples/fast_connect/virtual_circuit.tf

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ resource "oci_core_virtual_circuit" "virtual_circuit_public" {
4545
display_name = "${var.virtual_circuit_display_name}"
4646

4747
#provider_service_id = "${oci_core_provider_service.provider_service.id}"
48+
4849
public_prefixes = [
4950
{
5051
#Required
@@ -53,8 +54,10 @@ resource "oci_core_virtual_circuit" "virtual_circuit_public" {
5354
{
5455
cidr_block = "${var.virtual_circuit_public_prefixes_cidr_block2}"
5556
},
57+
{
58+
cidr_block = "${var.virtual_circuit_public_prefixes_cidr_block3}"
59+
},
5660
]
57-
5861
region = "${var.virtual_circuit_region}"
5962
}
6063

oci/core_virtual_circuit_resource.go

Lines changed: 131 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,11 @@ import (
66
"bytes"
77
"context"
88
"fmt"
9+
"strings"
910

1011
"github.com/hashicorp/terraform/helper/hashcode"
1112
"github.com/hashicorp/terraform/helper/schema"
1213

13-
"strings"
14-
1514
oci_core "github.com/oracle/oci-go-sdk/core"
1615
)
1716

@@ -107,16 +106,13 @@ func CoreVirtualCircuitResource() *schema.Resource {
107106
"public_prefixes": {
108107
Type: schema.TypeSet,
109108
Optional: true,
110-
Computed: true,
111-
ForceNew: true,
112109
Set: publicPrefixesHashCodeForSets,
113110
Elem: &schema.Resource{
114111
Schema: map[string]*schema.Schema{
115112
// Required
116113
"cidr_block": {
117114
Type: schema.TypeString,
118115
Required: true,
119-
ForceNew: true,
120116
},
121117

122118
// Optional
@@ -356,27 +352,35 @@ func (s *CoreVirtualCircuitResourceCrud) Get() error {
356352

357353
s.Res = &response.VirtualCircuit
358354

359-
// VirtualCircuitPublicPrefixes are returned from another API, make a List call on them if the VirtualCircuit is of type 'PUBLIC'
360-
if type_, ok := s.D.GetOkExists("type"); ok && strings.EqualFold(type_.(string), string(oci_core.VirtualCircuitTypePublic)) && s.Res.PublicPrefixes == nil {
361-
request2 := oci_core.ListVirtualCircuitPublicPrefixesRequest{}
362-
request2.VirtualCircuitId = request.VirtualCircuitId
363-
364-
response2, err2 := s.Client.ListVirtualCircuitPublicPrefixes(context.Background(), request2)
355+
ppRequest := oci_core.ListVirtualCircuitPublicPrefixesRequest{}
356+
ppRequest.RequestMetadata.RetryPolicy = getRetryPolicy(s.DisableNotFoundRetries, "core")
357+
ppRequest.VirtualCircuitId = request.VirtualCircuitId
365358

366-
publicPrefixes := []string{}
367-
for _, item := range response2.Items {
368-
publicPrefixes = append(publicPrefixes, *item.CidrBlock)
369-
}
359+
ppResponse, ppErr := s.Client.ListVirtualCircuitPublicPrefixes(context.Background(), ppRequest)
360+
if ppErr != nil {
361+
return ppErr
362+
}
370363

371-
s.Res.PublicPrefixes = publicPrefixes
372-
if err2 != nil {
373-
return err2
374-
}
364+
publicPrefixes := []string{}
365+
for _, item := range ppResponse.Items {
366+
publicPrefixes = append(publicPrefixes, *item.CidrBlock)
375367
}
368+
369+
s.Res.PublicPrefixes = publicPrefixes
370+
376371
return nil
377372
}
378373

379374
func (s *CoreVirtualCircuitResourceCrud) Update() error {
375+
// Update public prefixes, if changed
376+
// Cannot update PublicPrefix when the VirtualCircuit is in state PROVISIONING so public prefixes should be updated first
377+
if s.D.HasChange("public_prefixes") {
378+
err := s.updatePublicPrefixes()
379+
if err != nil {
380+
return fmt.Errorf("unable to update 'public_prefixes', error: %v", err)
381+
}
382+
}
383+
380384
request := oci_core.UpdateVirtualCircuitRequest{}
381385

382386
if bandwidthShapeName, ok := s.D.GetOkExists("bandwidth_shape_name"); ok {
@@ -435,6 +439,73 @@ func (s *CoreVirtualCircuitResourceCrud) Update() error {
435439
}
436440

437441
s.Res = &response.VirtualCircuit
442+
return nil
443+
}
444+
445+
// Update public prefixes using BulkAdd and BulkDelete APIs by computing the diff
446+
func (s *CoreVirtualCircuitResourceCrud) updatePublicPrefixes() error {
447+
virtualCircuitId := s.D.Id()
448+
449+
o, n := s.D.GetChange("public_prefixes")
450+
if o == nil {
451+
o = new(schema.Set)
452+
}
453+
if n == nil {
454+
n = new(schema.Set)
455+
}
456+
457+
os := o.(*schema.Set)
458+
ns := n.(*schema.Set)
459+
460+
newPublicPrefixesToAdd := ns.Difference(os).List()
461+
oldPublicPrefixesToDelete := os.Difference(ns).List()
462+
463+
var publicPrefixesToAdd []oci_core.CreateVirtualCircuitPublicPrefixDetails
464+
var publicPrefixesToDelete []oci_core.DeleteVirtualCircuitPublicPrefixDetails
465+
466+
for _, nppMap := range newPublicPrefixesToAdd {
467+
npp := mapToCreateVirtualCircuitPublicPrefixDetails(nppMap.(map[string]interface{}))
468+
publicPrefixesToAdd = append(publicPrefixesToAdd, npp)
469+
}
470+
471+
for _, oppMap := range oldPublicPrefixesToDelete {
472+
opp := mapToDeleteVirtualCircuitPublicPrefixDetails(oppMap.(map[string]interface{}))
473+
publicPrefixesToDelete = append(publicPrefixesToDelete, opp)
474+
}
475+
476+
// Add the public prefixes first, if any
477+
// And, wait for the update to complete before proceeding for subsequent updates if state is PROVISIONING
478+
if len(publicPrefixesToAdd) > 0 {
479+
addRequest := oci_core.BulkAddVirtualCircuitPublicPrefixesRequest{}
480+
addRequest.RequestMetadata.RetryPolicy = getRetryPolicy(s.DisableNotFoundRetries, "core")
481+
addRequest.PublicPrefixes = publicPrefixesToAdd
482+
addRequest.VirtualCircuitId = &virtualCircuitId
483+
addErr := s.Client.BulkAddVirtualCircuitPublicPrefixes(context.Background(), addRequest)
484+
if addErr != nil {
485+
return fmt.Errorf("failed to add public prefixes, error: %v", addErr)
486+
}
487+
488+
if waitErr := waitForUpdatedState(s.D, s); waitErr != nil {
489+
return waitErr
490+
}
491+
}
492+
493+
// Delete the old public prefixes, if any, after adding new ones
494+
// And, wait for the update to complete before proceeding for subsequent updates if state is PROVISIONING
495+
if len(publicPrefixesToDelete) > 0 {
496+
deleteRequest := oci_core.BulkDeleteVirtualCircuitPublicPrefixesRequest{}
497+
deleteRequest.RequestMetadata.RetryPolicy = getRetryPolicy(s.DisableNotFoundRetries, "core")
498+
deleteRequest.PublicPrefixes = publicPrefixesToDelete
499+
deleteRequest.VirtualCircuitId = &virtualCircuitId
500+
deleteErr := s.Client.BulkDeleteVirtualCircuitPublicPrefixes(context.Background(), deleteRequest)
501+
if deleteErr != nil {
502+
return fmt.Errorf("failed to delete public prefixes, error: %v", deleteErr)
503+
}
504+
505+
if waitErr := waitForUpdatedState(s.D, s); waitErr != nil {
506+
return waitErr
507+
}
508+
}
438509

439510
return nil
440511
}
@@ -492,13 +563,11 @@ func (s *CoreVirtualCircuitResourceCrud) SetData() error {
492563

493564
s.D.Set("provider_state", s.Res.ProviderState)
494565

495-
if s.Res.PublicPrefixes != nil {
496-
publicPrefixes := []interface{}{}
497-
for _, item := range s.Res.PublicPrefixes {
498-
publicPrefixes = append(publicPrefixes, CreateVirtualCircuitPublicPrefixDetailsToMap(item))
499-
}
500-
s.D.Set("public_prefixes", schema.NewSet(publicPrefixesHashCodeForSets, publicPrefixes))
566+
publicPrefixes := []interface{}{}
567+
for _, item := range s.Res.PublicPrefixes {
568+
publicPrefixes = append(publicPrefixes, CreateVirtualCircuitPublicPrefixDetailsToMap(item))
501569
}
570+
s.D.Set("public_prefixes", schema.NewSet(publicPrefixesHashCodeForSets, publicPrefixes))
502571

503572
if s.Res.ReferenceComment != nil {
504573
s.D.Set("reference_comment", *s.Res.ReferenceComment)
@@ -521,6 +590,30 @@ func (s *CoreVirtualCircuitResourceCrud) SetData() error {
521590
return nil
522591
}
523592

593+
// Converting raw set data from state diff to DeleteVirtualCircuitPublicPrefixDetails
594+
func mapToDeleteVirtualCircuitPublicPrefixDetails(publicPrefix map[string]interface{}) oci_core.DeleteVirtualCircuitPublicPrefixDetails {
595+
result := oci_core.DeleteVirtualCircuitPublicPrefixDetails{}
596+
597+
if cidrBlock, ok := publicPrefix["cidr_block"]; ok {
598+
tmp := cidrBlock.(string)
599+
result.CidrBlock = &tmp
600+
}
601+
602+
return result
603+
}
604+
605+
// Converting raw set data from state diff to CreateVirtualCircuitPublicPrefixDetails
606+
func mapToCreateVirtualCircuitPublicPrefixDetails(publicPrefix map[string]interface{}) oci_core.CreateVirtualCircuitPublicPrefixDetails {
607+
result := oci_core.CreateVirtualCircuitPublicPrefixDetails{}
608+
609+
if cidrBlock, ok := publicPrefix["cidr_block"]; ok {
610+
tmp := cidrBlock.(string)
611+
result.CidrBlock = &tmp
612+
}
613+
614+
return result
615+
}
616+
524617
func (s *CoreVirtualCircuitResourceCrud) mapToCreateVirtualCircuitPublicPrefixDetails(fieldKeyFormat string) (oci_core.CreateVirtualCircuitPublicPrefixDetails, error) {
525618
result := oci_core.CreateVirtualCircuitPublicPrefixDetails{}
526619

@@ -553,20 +646,24 @@ func (s *CoreVirtualCircuitResourceCrud) mapToCrossConnectMapping(fieldKeyFormat
553646
result.CrossConnectOrCrossConnectGroupId = &tmp
554647
}
555648

556-
if customerBgpPeeringIp, ok := s.D.GetOkExists(fmt.Sprintf(fieldKeyFormat, "customer_bgp_peering_ip")); ok {
557-
tmp := customerBgpPeeringIp.(string)
558-
result.CustomerBgpPeeringIp = &tmp
559-
}
649+
// customer_bgp_peering_ip, oracleBgpPeeringIp are not allowed in requests for PUBLIC virtual circuit
650+
if vcType, ok := s.D.GetOkExists("type"); ok && !strings.EqualFold(vcType.(string), string(oci_core.VirtualCircuitTypePublic)) {
651+
if customerBgpPeeringIp, ok := s.D.GetOkExists(fmt.Sprintf(fieldKeyFormat, "customer_bgp_peering_ip")); ok {
652+
tmp := customerBgpPeeringIp.(string)
653+
result.CustomerBgpPeeringIp = &tmp
654+
}
560655

561-
if oracleBgpPeeringIp, ok := s.D.GetOkExists(fmt.Sprintf(fieldKeyFormat, "oracle_bgp_peering_ip")); ok {
562-
tmp := oracleBgpPeeringIp.(string)
563-
result.OracleBgpPeeringIp = &tmp
656+
if oracleBgpPeeringIp, ok := s.D.GetOkExists(fmt.Sprintf(fieldKeyFormat, "oracle_bgp_peering_ip")); ok {
657+
tmp := oracleBgpPeeringIp.(string)
658+
result.OracleBgpPeeringIp = &tmp
659+
}
564660
}
565661

566662
if vlan, ok := s.D.GetOkExists(fmt.Sprintf(fieldKeyFormat, "vlan")); ok {
567663
tmp := vlan.(int)
568-
//Vlan value must be greater than or equal to 100. It cannot be specified for certain circuit types.
569-
if tmp >= 100 {
664+
// Vlan value must be greater than or equal to 100.
665+
// It cannot be specified for certain circuit types, hence protecting against default '0' values
666+
if tmp > 0 {
570667
result.Vlan = &tmp
571668
}
572669
}

oci/core_virtual_circuit_test.go

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ var (
9191
"oracle_bgp_peering_ip": Representation{repType: Required, create: `10.0.0.19/31`, update: `10.0.0.21/31`},
9292
}
9393
virtualCircuitPublicPrefixesRepresentation = map[string]interface{}{
94-
"cidr_block": Representation{repType: Required, create: `0.0.0.0/5`},
94+
"cidr_block": Representation{repType: Required, create: `0.0.0.0/5`, update: `0.0.0.0/6`},
9595
}
9696

9797
VirtualCircuitWithProviderResourceConfigFilter = `
@@ -175,6 +175,32 @@ func TestCoreVirtualCircuitResource_basic(t *testing.T) {
175175
},
176176
),
177177
},
178+
// verify update - PUBLIC Virtual Circuit
179+
{
180+
Config: config + VirtualCircuitPublicPropertyVariables + compartmentIdVariableStr + VirtualCircuitResourceDependencies +
181+
generateResourceFromRepresentationMap("oci_core_virtual_circuit", "test_virtual_circuit", Required, Update, virtualCircuitPublicRequiredOnlyRepresentation),
182+
Check: resource.ComposeAggregateTestCheckFunc(
183+
resource.TestCheckResourceAttr(resourceName, "compartment_id", compartmentId),
184+
resource.TestCheckResourceAttr(resourceName, "cross_connect_mappings.#", "1"),
185+
resource.TestCheckResourceAttrSet(resourceName, "cross_connect_mappings.0.cross_connect_or_cross_connect_group_id"),
186+
resource.TestCheckResourceAttr(resourceName, "cross_connect_mappings.0.vlan", "300"),
187+
resource.TestCheckResourceAttr(resourceName, "customer_bgp_asn", "11"),
188+
resource.TestCheckResourceAttr(resourceName, "public_prefixes.#", "1"),
189+
CheckResourceSetContainsElementWithProperties(resourceName, "public_prefixes", map[string]string{
190+
"cidr_block": "0.0.0.0/6",
191+
},
192+
[]string{}),
193+
resource.TestCheckResourceAttr(resourceName, "type", "PUBLIC"),
194+
195+
func(s *terraform.State) (err error) {
196+
resId2, err = fromInstanceState(s, resourceName, "id")
197+
if resId != resId2 {
198+
return fmt.Errorf("Resource recreated when it was supposed to be updated.")
199+
}
200+
return err
201+
},
202+
),
203+
},
178204
// delete before next create
179205
{
180206
Config: config + compartmentIdVariableStr + VirtualCircuitResourceDependencies,
@@ -341,14 +367,11 @@ func TestCoreVirtualCircuitResource_basic(t *testing.T) {
341367
},
342368
// verify resource import
343369
{
344-
Config: config + compartmentIdVariableStr + VirtualCircuitResourceDependencies + VirtualCircuitPrivatePropertyVariables +
345-
generateResourceFromRepresentationMap("oci_core_virtual_circuit", "test_virtual_circuit", Optional, Update, virtualCircuitRepresentation),
370+
Config: config,
346371
ImportState: true,
347372
ImportStateVerify: true,
348373
ImportStateVerifyIgnore: []string{
349-
"public_prefixes",
350374
"region",
351-
"virtual_circuit_id",
352375
},
353376
ResourceName: resourceName,
354377
},

oci/crud_helpers.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,18 @@ func stateRefreshFunc(sync StatefulResource) resource.StateRefreshFunc {
372372
}
373373
}
374374

375+
// Helper function to wait for update to reach terminal state before doing another update
376+
// Useful in situations where more than one update is needed and prior update needs to complete
377+
func waitForUpdatedState(d *schema.ResourceData, sync ResourceUpdater) error {
378+
if stateful, ok := sync.(StatefullyUpdatedResource); ok {
379+
if e := waitForStateRefresh(stateful, d.Timeout(schema.TimeoutUpdate), "update", stateful.UpdatedPending(), stateful.UpdatedTarget()); e != nil {
380+
return e
381+
}
382+
}
383+
384+
return nil
385+
}
386+
375387
// waitForStateRefresh takes a StatefulResource, a timeout duration, a list of states to treat as Pending, and a list of states to treat as Target. It uses those to wrap resource.StateChangeConf.WaitForState(). If the resource returns a missing status, it will not be treated as an error.
376388
//
377389
// sync.D.Id must be set.

0 commit comments

Comments
 (0)