From 65d68883a92cde6a1818dcaddad89abd9e2e3708 Mon Sep 17 00:00:00 2001 From: Ayush Rangwala Date: Fri, 18 Aug 2023 23:24:43 +0530 Subject: [PATCH 1/5] Upgrade metal device resource and data source to use timeouts --- equinix/resource_metal_device.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/equinix/resource_metal_device.go b/equinix/resource_metal_device.go index 1596ff188..e104334da 100644 --- a/equinix/resource_metal_device.go +++ b/equinix/resource_metal_device.go @@ -806,8 +806,8 @@ func doReinstall(ctx context.Context, client *packngo.Client, d *schema.Resource return friendlyError(err) } - updateTimeout := d.Timeout(schema.TimeoutUpdate) - 30*time.Second - time.Since(start) - if err := waitForActiveDevice(ctx, d, meta, updateTimeout); err != nil { + deleteTimeout := d.Timeout(schema.TimeoutUpdate) - 30*time.Second - time.Since(start) + if err := waitForActiveDevice(ctx, d, meta, deleteTimeout); err != nil { return err } } From 24b4dffb595f32e095f285eb5db26c0f67d85124 Mon Sep 17 00:00:00 2001 From: Ayush Rangwala Date: Wed, 23 Aug 2023 19:31:32 +0530 Subject: [PATCH 2/5] Acceptance tests for metal device timeouts --- equinix/resource_network_bgp_test.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/equinix/resource_network_bgp_test.go b/equinix/resource_network_bgp_test.go index b4131f3c1..f40bafb6b 100644 --- a/equinix/resource_network_bgp_test.go +++ b/equinix/resource_network_bgp_test.go @@ -2,6 +2,7 @@ package equinix import ( "context" + "github.com/packethost/packngo" "testing" "time" @@ -100,6 +101,20 @@ func (r *mockedBGPUpdateRequest) Execute() error { return nil } +var _ packngo.BGPConfigService = &mockBgpConfigService{} + +type mockBgpConfigService struct { + GetFn func(projectID string, getOpt *packngo.GetOptions) (*packngo.BGPConfig, *packngo.Response, error) + CreateFn func(projectID string, request packngo.CreateBGPConfigRequest) (*packngo.Response, error) +} + +func (s *mockBgpConfigService) Get(projectID string, getOpt *packngo.GetOptions) (*packngo.BGPConfig, *packngo.Response, error) { + return s.GetFn(projectID, getOpt) +} +func (s *mockBgpConfigService) Create(projectID string, request packngo.CreateBGPConfigRequest) (*packngo.Response, error) { + return s.CreateFn(projectID, request) +} + func TestNetworkBGP_createUpdateRequest(t *testing.T) { // given req := &mockedBGPUpdateRequest{data: make(map[string]interface{})} From 09b19b3c84d1441cf25f9da28eba9924d94665bd Mon Sep 17 00:00:00 2001 From: Ayush Rangwala Date: Thu, 24 Aug 2023 18:49:13 +0530 Subject: [PATCH 3/5] Timeouts tests to be unit test to be able to run without TF_ACC --- equinix/resource_network_bgp_test.go | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/equinix/resource_network_bgp_test.go b/equinix/resource_network_bgp_test.go index f40bafb6b..b4131f3c1 100644 --- a/equinix/resource_network_bgp_test.go +++ b/equinix/resource_network_bgp_test.go @@ -2,7 +2,6 @@ package equinix import ( "context" - "github.com/packethost/packngo" "testing" "time" @@ -101,20 +100,6 @@ func (r *mockedBGPUpdateRequest) Execute() error { return nil } -var _ packngo.BGPConfigService = &mockBgpConfigService{} - -type mockBgpConfigService struct { - GetFn func(projectID string, getOpt *packngo.GetOptions) (*packngo.BGPConfig, *packngo.Response, error) - CreateFn func(projectID string, request packngo.CreateBGPConfigRequest) (*packngo.Response, error) -} - -func (s *mockBgpConfigService) Get(projectID string, getOpt *packngo.GetOptions) (*packngo.BGPConfig, *packngo.Response, error) { - return s.GetFn(projectID, getOpt) -} -func (s *mockBgpConfigService) Create(projectID string, request packngo.CreateBGPConfigRequest) (*packngo.Response, error) { - return s.CreateFn(projectID, request) -} - func TestNetworkBGP_createUpdateRequest(t *testing.T) { // given req := &mockedBGPUpdateRequest{data: make(map[string]interface{})} From 590b6d4ff6bb9412b02b6a1c92e2cf7c2d0c84ec Mon Sep 17 00:00:00 2001 From: Ayush Rangwala Date: Wed, 30 Aug 2023 19:11:38 +0530 Subject: [PATCH 4/5] Testing Update and create --- equinix/resource_metal_device.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/equinix/resource_metal_device.go b/equinix/resource_metal_device.go index e104334da..1596ff188 100644 --- a/equinix/resource_metal_device.go +++ b/equinix/resource_metal_device.go @@ -806,8 +806,8 @@ func doReinstall(ctx context.Context, client *packngo.Client, d *schema.Resource return friendlyError(err) } - deleteTimeout := d.Timeout(schema.TimeoutUpdate) - 30*time.Second - time.Since(start) - if err := waitForActiveDevice(ctx, d, meta, deleteTimeout); err != nil { + updateTimeout := d.Timeout(schema.TimeoutUpdate) - 30*time.Second - time.Since(start) + if err := waitForActiveDevice(ctx, d, meta, updateTimeout); err != nil { return err } } From b4c03448afa8637b2ef4d33cad112fa1152be5cf Mon Sep 17 00:00:00 2001 From: Ayush Rangwala Date: Tue, 19 Sep 2023 12:09:41 +0530 Subject: [PATCH 5/5] Support create IP block timeout --- equinix/resource_metal_reserved_ip_block.go | 30 +++++------ ...source_metal_reserved_ip_block_acc_test.go | 50 +++++++++++++++---- 2 files changed, 56 insertions(+), 24 deletions(-) diff --git a/equinix/resource_metal_reserved_ip_block.go b/equinix/resource_metal_reserved_ip_block.go index e70455a2d..04ff8c593 100644 --- a/equinix/resource_metal_reserved_ip_block.go +++ b/equinix/resource_metal_reserved_ip_block.go @@ -1,6 +1,7 @@ package equinix import ( + "context" "encoding/json" "fmt" "log" @@ -215,12 +216,12 @@ func resourceMetalReservedIPBlock() *schema.Resource { } // TODO: add comments field, used for reservations that are not automatically approved return &schema.Resource{ - Create: resourceMetalReservedIPBlockCreate, - Read: resourceMetalReservedIPBlockRead, - Update: resourceMetalReservedIPBlockUpdate, - Delete: resourceMetalReservedIPBlockDelete, + CreateContext: diagnosticsWrapper(resourceMetalReservedIPBlockCreate), + ReadWithoutTimeout: diagnosticsWrapper(resourceMetalReservedIPBlockRead), + UpdateWithoutTimeout: diagnosticsWrapper(resourceMetalReservedIPBlockUpdate), + DeleteWithoutTimeout: diagnosticsWrapper(resourceMetalReservedIPBlockDelete), Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, + StateContext: schema.ImportStatePassthroughContext, }, Schema: reservedBlockSchema, @@ -230,14 +231,14 @@ func resourceMetalReservedIPBlock() *schema.Resource { } } -func resourceMetalReservedIPBlockCreate(d *schema.ResourceData, meta interface{}) error { +func resourceMetalReservedIPBlockCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) error { meta.(*Config).addModuleToMetalUserAgent(d) client := meta.(*Config).metal quantity := d.Get("quantity").(int) typ := d.Get("type").(string) - req := packngo.IPReservationRequest{ + req := packngo.IPReservationCreateRequest{ Type: packngo.IPReservationType(typ), Quantity: quantity, } @@ -285,6 +286,7 @@ func resourceMetalReservedIPBlockCreate(d *schema.ResourceData, meta interface{} req.Network = d.Get("network").(string) req.CIDR = d.Get("cidr").(int) + start := time.Now() blockAddr, _, err := client.ProjectIPs.Create(projectID, &req) if err != nil { return fmt.Errorf("error reserving IP address block: %s", err) @@ -302,17 +304,17 @@ func resourceMetalReservedIPBlockCreate(d *schema.ResourceData, meta interface{} Pending: []string{string(packngo.IPReservationStatePending)}, Target: target, Refresh: reservedIPStateRefreshFunc(client, d.Id()), - Timeout: d.Timeout(schema.TimeoutCreate), + Timeout: d.Timeout(schema.TimeoutCreate) - 30*time.Second - time.Since(start), MinTimeout: 15 * time.Second, } - if _, err := stateConf.WaitForState(); err != nil { + if _, err := stateConf.WaitForStateContext(ctx); err != nil { return fmt.Errorf("error waiting for IP Reservation (%s) to become %s: %s", d.Id(), wfs, err) } - return resourceMetalReservedIPBlockRead(d, meta) + return resourceMetalReservedIPBlockRead(ctx, d, meta) } -func resourceMetalReservedIPBlockUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceMetalReservedIPBlockUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) error { meta.(*Config).addModuleToMetalUserAgent(d) client := meta.(*Config).metal id := d.Id() @@ -344,7 +346,7 @@ func resourceMetalReservedIPBlockUpdate(d *schema.ResourceData, meta interface{} return fmt.Errorf("error updating IP reservation: %w", err) } - return resourceMetalReservedIPBlockRead(d, meta) + return resourceMetalReservedIPBlockRead(ctx, d, meta) } func reservedIPStateRefreshFunc(client *packngo.Client, reservedIPId string) retry.StateRefreshFunc { @@ -454,7 +456,7 @@ func loadBlock(d *schema.ResourceData, reservedBlock *packngo.IPAddressReservati return setMap(d, attributeMap) } -func resourceMetalReservedIPBlockRead(d *schema.ResourceData, meta interface{}) error { +func resourceMetalReservedIPBlockRead(ctx context.Context, d *schema.ResourceData, meta interface{}) error { meta.(*Config).addModuleToMetalUserAgent(d) client := meta.(*Config).metal @@ -485,7 +487,7 @@ func resourceMetalReservedIPBlockRead(d *schema.ResourceData, meta interface{}) return nil } -func resourceMetalReservedIPBlockDelete(d *schema.ResourceData, meta interface{}) error { +func resourceMetalReservedIPBlockDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) error { meta.(*Config).addModuleToMetalUserAgent(d) client := meta.(*Config).metal diff --git a/equinix/resource_metal_reserved_ip_block_acc_test.go b/equinix/resource_metal_reserved_ip_block_acc_test.go index 671f69832..f10143592 100644 --- a/equinix/resource_metal_reserved_ip_block_acc_test.go +++ b/equinix/resource_metal_reserved_ip_block_acc_test.go @@ -3,6 +3,7 @@ package equinix import ( "bytes" "fmt" + "regexp" "testing" "text/template" @@ -11,6 +12,10 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) +var ( + matchIpBlockErrTimeout = regexp.MustCompile(".* timeout while waiting for state to become 'created'.*") +) + func testAccMetalReservedIPBlockConfig_global(name string) string { return fmt.Sprintf(` resource "equinix_metal_project" "foobar" { @@ -28,7 +33,11 @@ resource "equinix_metal_reserved_ip_block" "test" { }`, name, name) } -func testAccMetalReservedIPBlockConfig_public(name string) string { +func testAccMetalReservedIPBlockConfig_public(name, createTimeout string) string { + if createTimeout == "" { + createTimeout = "20m" + } + return fmt.Sprintf(` resource "equinix_metal_project" "foobar" { name = "tfacc-reserved_ip_block-%s" @@ -41,7 +50,11 @@ resource "equinix_metal_reserved_ip_block" "test" { description = "tfacc-reserved_ip_block-%s" quantity = 2 tags = ["Tag1", "Tag2"] -}`, name, name) + + timeouts { + create = "%s" + } +}`, name, name, createTimeout) } // testAccMetalReservedIPBlockConfig_metro generates a config for a metro IP @@ -74,7 +87,7 @@ func TestAccMetalReservedIPBlock_global(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ExternalProviders: testExternalProviders, - Providers: testAccProviders, + ProviderFactories: testAccProviderFactories, CheckDestroy: testAccMetalReservedIPBlockCheckDestroyed, Steps: []resource.TestStep{ { @@ -106,11 +119,11 @@ func TestAccMetalReservedIPBlock_public(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ExternalProviders: testExternalProviders, - Providers: testAccProviders, + ProviderFactories: testAccProviderFactories, CheckDestroy: testAccMetalReservedIPBlockCheckDestroyed, Steps: []resource.TestStep{ { - Config: testAccMetalReservedIPBlockConfig_public(rs), + Config: testAccMetalReservedIPBlockConfig_public(rs, ""), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr( "equinix_metal_reserved_ip_block.test", "facility", "ny5"), @@ -141,7 +154,7 @@ func TestAccMetalReservedIPBlock_metro(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ExternalProviders: testExternalProviders, - Providers: testAccProviders, + ProviderFactories: testAccProviderFactories, CheckDestroy: testAccMetalReservedIPBlockCheckDestroyed, Steps: []resource.TestStep{ { @@ -180,11 +193,11 @@ func TestAccMetalReservedIPBlock_importBasic(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ExternalProviders: testExternalProviders, - Providers: testAccProviders, + ProviderFactories: testAccProviderFactories, CheckDestroy: testAccMetalReservedIPBlockCheckDestroyed, Steps: []resource.TestStep{ { - Config: testAccMetalReservedIPBlockConfig_public(rs), + Config: testAccMetalReservedIPBlockConfig_public(rs, ""), }, { ResourceName: "equinix_metal_reserved_ip_block.test", @@ -230,7 +243,7 @@ func TestAccMetalReservedIPBlock_facilityToMetro(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ExternalProviders: testExternalProviders, - Providers: testAccProviders, + ProviderFactories: testAccProviderFactories, CheckDestroy: testAccMetalReservedIPBlockCheckDestroyed, Steps: []resource.TestStep{ { @@ -295,7 +308,7 @@ func TestAccMetalReservedIPBlock_device(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ExternalProviders: testExternalProviders, - Providers: testAccProviders, + ProviderFactories: testAccProviderFactories, CheckDestroy: testAccMetalReservedIPBlockCheckDestroyed, Steps: []resource.TestStep{ { @@ -310,3 +323,20 @@ func TestAccMetalReservedIPBlock_device(t *testing.T) { }, }) } + +func TestAccMetalReservedIPBlockCreate_public_timeout(t *testing.T) { + rs := acctest.RandString(10) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ExternalProviders: testExternalProviders, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccMetalReservedIPBlockCheckDestroyed, + Steps: []resource.TestStep{ + { + Config: testAccMetalReservedIPBlockConfig_public(rs, "2s"), + ExpectError: matchIpBlockErrTimeout, + }, + }, + }) +}