diff --git a/equinix/acceptance/acceptance.go b/equinix/acceptance/acceptance.go new file mode 100644 index 000000000..0af75a05e --- /dev/null +++ b/equinix/acceptance/acceptance.go @@ -0,0 +1,83 @@ +package acceptance + +import ( + "fmt" + "os" + "strconv" + "strings" + "testing" + "time" + + "github.com/equinix/terraform-provider-equinix/equinix" + "github.com/equinix/terraform-provider-equinix/internal/config" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +const ( + // duplicated from equinix_sweeoer_test.go + tstResourcePrefix = "tfacc" + missingMetalToken = "To run acceptance tests of Equinix Metal Resources, you must set %s" +) + +var ( + TestAccProvider *schema.Provider + TestAccProviders map[string]*schema.Provider + TestAccProviderFactories map[string]func() (*schema.Provider, error) + TestExternalProviders map[string]resource.ExternalProvider +) + +func init() { + TestAccProvider = equinix.Provider() + TestAccProviders = map[string]*schema.Provider{ + "equinix": TestAccProvider, + } + TestAccProviderFactories = map[string]func() (*schema.Provider, error){ + "equinix": func() (*schema.Provider, error) { + return TestAccProvider, nil + }, + } + TestExternalProviders = map[string]resource.ExternalProvider{ + "random": { + Source: "hashicorp/random", + }, + } +} + +func TestAccPreCheckMetal(t *testing.T) { + if os.Getenv(equinix.MetalAuthTokenEnvVar) == "" { + t.Fatalf(missingMetalToken, equinix.MetalAuthTokenEnvVar) + } +} + +func IsSweepableTestResource(namePrefix string) bool { + return strings.HasPrefix(namePrefix, tstResourcePrefix) +} + +func getFromEnvDefault(varName string, defaultValue string) string { + if v := os.Getenv(varName); v != "" { + return v + } + return defaultValue +} + +func GetConfigForNonStandardMetalTest() (*config.Config, error) { + endpoint := getFromEnvDefault(equinix.EndpointEnvVar, config.DefaultBaseURL) + clientTimeout := getFromEnvDefault(equinix.ClientTimeoutEnvVar, strconv.Itoa(config.DefaultTimeout)) + clientTimeoutInt, err := strconv.Atoi(clientTimeout) + if err != nil { + return nil, fmt.Errorf("cannot convert value of '%s' env variable to int", equinix.ClientTimeoutEnvVar) + } + metalAuthToken := getFromEnvDefault(equinix.MetalAuthTokenEnvVar, "") + + if metalAuthToken == "" { + return nil, fmt.Errorf(missingMetalToken, equinix.MetalAuthTokenEnvVar) + } + + return &config.Config{ + AuthToken: metalAuthToken, + BaseURL: endpoint, + RequestTimeout: time.Duration(clientTimeoutInt) * time.Second, + }, nil +} diff --git a/equinix/acceptance/device_helpers.go b/equinix/acceptance/device_helpers.go new file mode 100644 index 000000000..43805f207 --- /dev/null +++ b/equinix/acceptance/device_helpers.go @@ -0,0 +1,119 @@ +package acceptance + +import ( + "fmt" + "strings" + "time" +) + +// list of plans and metros and os used as filter criteria to find available hardware to run tests +var ( + Preferable_plans = []string{"x1.small.x86", "t1.small.x86", "c2.medium.x86", "c3.small.x86", "c3.medium.x86", "m3.small.x86"} + Preferable_metros = []string{"ch", "ny", "sv", "ty", "am"} + Preferable_os = []string{"ubuntu_20_04"} +) + +func TestDeviceTerminationTime() string { + return time.Now().UTC().Add(60 * time.Minute).Format(time.RFC3339) +} + +// This function should be used to find available plans in all test where a metal_device resource is needed. +// +// TODO consider adding a datasource for equinix_metal_operating_system and making the local.os conditional +// +// https://github.com/equinix/terraform-provider-equinix/pull/220#discussion_r915418418equinix_metal_operating_system +// https://github.com/equinix/terraform-provider-equinix/discussions/221 +func ConfAccMetalDevice_base(plans, metros, os []string) string { + return fmt.Sprintf(` +data "equinix_metal_plans" "test" { + sort { + attribute = "id" + direction = "asc" + } + + filter { + attribute = "name" + values = [%s] + } + filter { + attribute = "available_in_metros" + values = [%s] + } + filter { + attribute = "deployment_types" + values = ["on_demand", "spot_market"] + } +} + +// Select a metal plan randomly and lock it in +// so that we don't pick a different one for +// every subsequent terraform plan +resource "random_integer" "plan_idx" { + min = 0 + max = length(data.equinix_metal_plans.test.plans) - 1 +} + +resource "terraform_data" "plan" { + input = data.equinix_metal_plans.test.plans[random_integer.plan_idx.result] + + lifecycle { + ignore_changes = ["input"] + } +} + +resource "terraform_data" "facilities" { + input = sort(tolist(setsubtract(terraform_data.plan.output.available_in, ["nrt1", "dfw2", "ewr1", "ams1", "sjc1", "ld7", "sy4", "ny6"]))) + + lifecycle { + ignore_changes = ["input"] + } +} + +// Select a metal facility randomly and lock it in +// so that we don't pick a different one for +// every subsequent terraform plan +resource "random_integer" "facility_idx" { + min = 0 + max = length(local.facilities) - 1 +} + +resource "terraform_data" "facility" { + input = local.facilities[random_integer.facility_idx.result] + + lifecycle { + ignore_changes = ["input"] + } +} + +// Select a metal metro randomly and lock it in +// so that we don't pick a different one for +// every subsequent terraform plan +resource "random_integer" "metro_idx" { + min = 0 + max = length(local.metros) - 1 +} + +resource "terraform_data" "metro" { + input = local.metros[random_integer.metro_idx.result] + + lifecycle { + ignore_changes = ["input"] + } +} + +locals { + // Select a random plan + plan = terraform_data.plan.output.slug + + // Select a random facility from the facilities in which the selected plan is available, excluding decommed facilities + facilities = terraform_data.facilities.output + facility = terraform_data.facility.output + + // Select a random metro from the metros in which the selected plan is available + metros = sort(tolist(terraform_data.plan.output.available_in_metros)) + metro = terraform_data.metro.output + + os = [%s][0] +} +`, fmt.Sprintf("\"%s\"", strings.Join(plans[:], `","`)), fmt.Sprintf("\"%s\"", strings.Join(metros[:], `","`)), fmt.Sprintf("\"%s\"", strings.Join(os[:], `","`))) +} diff --git a/equinix/acceptance/ssh_key_helpers.go b/equinix/acceptance/ssh_key_helpers.go new file mode 100644 index 000000000..75606c896 --- /dev/null +++ b/equinix/acceptance/ssh_key_helpers.go @@ -0,0 +1,36 @@ +package acceptance + +import ( + "fmt" + + "github.com/equinix/terraform-provider-equinix/internal/config" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/packethost/packngo" +) + +func TestAccCheckMetalSSHKeyExists(n string, key *packngo.SSHKey) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + if rs.Primary.ID == "" { + return fmt.Errorf("No Record ID is set") + } + + client := TestAccProvider.Meta().(*config.Config).Metal + + foundKey, _, err := client.SSHKeys.Get(rs.Primary.ID, nil) + if err != nil { + return err + } + if foundKey.ID != rs.Primary.ID { + return fmt.Errorf("SSh Key not found: %v - %v", rs.Primary.ID, foundKey) + } + + *key = *foundKey + + return nil + } +} diff --git a/equinix/data_source_ecx_l2_sellerprofiles.go b/equinix/data_source_ecx_l2_sellerprofiles.go index 2104f9b42..2aa5f3562 100644 --- a/equinix/data_source_ecx_l2_sellerprofiles.go +++ b/equinix/data_source_ecx_l2_sellerprofiles.go @@ -6,6 +6,7 @@ import ( "regexp" "strings" + "github.com/equinix/terraform-provider-equinix/internal" "github.com/equinix/terraform-provider-equinix/internal/config" "github.com/equinix/ecx-go/v2" @@ -98,8 +99,8 @@ func dataSourceECXL2SellerProfilesRead(ctx context.Context, d *schema.ResourceDa } var filteredProfiles []ecx.L2ServiceProfile nameRegex := d.Get(ecxL2SellerProfilesSchemaNames["NameRegex"]).(string) - metros := expandSetToStringList(d.Get(ecxL2SellerProfilesSchemaNames["Metros"]).(*schema.Set)) - speedBands := expandSetToStringList(d.Get(ecxL2SellerProfilesSchemaNames["SpeedBands"]).(*schema.Set)) + metros := internal.ExpandSetToStringList(d.Get(ecxL2SellerProfilesSchemaNames["Metros"]).(*schema.Set)) + speedBands := internal.ExpandSetToStringList(d.Get(ecxL2SellerProfilesSchemaNames["SpeedBands"]).(*schema.Set)) orgName := d.Get(ecxL2SellerProfilesSchemaNames["OrganizationName"]).(string) globalOrgName := d.Get(ecxL2SellerProfilesSchemaNames["GlobalOrganization"]).(string) for _, profile := range profiles { diff --git a/equinix/data_source_metal_device.go b/equinix/data_source_metal_device.go index 8e7e9ab90..41b74161d 100644 --- a/equinix/data_source_metal_device.go +++ b/equinix/data_source_metal_device.go @@ -8,6 +8,7 @@ import ( "sort" "strings" + "github.com/equinix/terraform-provider-equinix/internal" "github.com/equinix/terraform-provider-equinix/internal/config" metalv1 "github.com/equinix-labs/metal-go/metal/v1" @@ -278,7 +279,7 @@ func dataSourceMetalDeviceRead(ctx context.Context, d *schema.ResourceData, meta if device.HardwareReservation != nil { d.Set("hardware_reservation_id", device.HardwareReservation.GetId()) } - networkType, err := getNetworkType(device) + networkType, err := internal.GetNetworkType(device) if err != nil { return fmt.Errorf("[ERR] Error computing network type for device (%s): %s", d.Id(), err) } @@ -292,14 +293,14 @@ func dataSourceMetalDeviceRead(ctx context.Context, d *schema.ResourceData, meta keyIDs = append(keyIDs, path.Base(k.Href)) } d.Set("ssh_key_ids", keyIDs) - networkInfo := getNetworkInfo(device.IpAddresses) + networkInfo := internal.GetNetworkInfo(device.IpAddresses) sort.SliceStable(networkInfo.Networks, func(i, j int) bool { famI := networkInfo.Networks[i]["family"].(int32) famJ := networkInfo.Networks[j]["family"].(int32) pubI := networkInfo.Networks[i]["public"].(bool) pubJ := networkInfo.Networks[j]["public"].(bool) - return getNetworkRank(int(famI), pubI) < getNetworkRank(int(famJ), pubJ) + return internal.GetNetworkRank(int(famI), pubI) < internal.GetNetworkRank(int(famJ), pubJ) }) d.Set("network", networkInfo.Networks) @@ -307,7 +308,7 @@ func dataSourceMetalDeviceRead(ctx context.Context, d *schema.ResourceData, meta d.Set("access_private_ipv4", networkInfo.PrivateIPv4) d.Set("access_public_ipv6", networkInfo.PublicIPv6) - ports := getPorts(device.NetworkPorts) + ports := internal.GetPorts(device.NetworkPorts) d.Set("ports", ports) d.SetId(device.GetId()) diff --git a/equinix/data_source_metal_devices.go b/equinix/data_source_metal_devices.go index 9e1d834d5..e52eecef1 100644 --- a/equinix/data_source_metal_devices.go +++ b/equinix/data_source_metal_devices.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/equinix/terraform-provider-equinix/internal" "github.com/equinix/terraform-provider-equinix/internal/config" metalv1 "github.com/equinix-labs/metal-go/metal/v1" @@ -95,5 +96,5 @@ func flattenDevice(rawDevice interface{}, meta interface{}, extra map[string]int if !ok { return nil, fmt.Errorf("expected device to be of type *metalv1.Device, got %T", rawDevice) } - return getDeviceMap(device), nil + return internal.GetDeviceMap(device), nil } diff --git a/equinix/data_source_network_device_software.go b/equinix/data_source_network_device_software.go index 616b5bf65..fcb689719 100644 --- a/equinix/data_source_network_device_software.go +++ b/equinix/data_source_network_device_software.go @@ -7,6 +7,7 @@ import ( "sort" "time" + "github.com/equinix/terraform-provider-equinix/internal" "github.com/equinix/terraform-provider-equinix/internal/config" "github.com/equinix/ne-go" @@ -116,7 +117,7 @@ func dataSourceNetworkDeviceSoftwareRead(ctx context.Context, d *schema.Resource conf := m.(*config.Config) var diags diag.Diagnostics typeCode := d.Get(networkDeviceSoftwareSchemaNames["DeviceTypeCode"]).(string) - pkgCodes := expandSetToStringList(d.Get(networkDeviceSoftwareSchemaNames["PackageCodes"]).(*schema.Set)) + pkgCodes := internal.ExpandSetToStringList(d.Get(networkDeviceSoftwareSchemaNames["PackageCodes"]).(*schema.Set)) versions, err := conf.Ne.GetDeviceSoftwareVersions(typeCode) if err != nil { return diag.FromErr(err) diff --git a/equinix/data_source_network_device_type.go b/equinix/data_source_network_device_type.go index 098d11cb9..0df4666e4 100644 --- a/equinix/data_source_network_device_type.go +++ b/equinix/data_source_network_device_type.go @@ -5,6 +5,8 @@ import ( "fmt" "strings" + "github.com/equinix/terraform-provider-equinix/internal" + "github.com/equinix/terraform-provider-equinix/internal/config" "github.com/equinix/ne-go" @@ -89,7 +91,7 @@ func dataSourceNetworkDeviceTypeRead(ctx context.Context, d *schema.ResourceData name := d.Get(networkDeviceTypeSchemaNames["Name"]).(string) vendor := d.Get(networkDeviceTypeSchemaNames["Vendor"]).(string) category := d.Get(networkDeviceTypeSchemaNames["Category"]).(string) - metroCodes := expandSetToStringList(d.Get(networkDeviceTypeSchemaNames["MetroCodes"]).(*schema.Set)) + metroCodes := internal.ExpandSetToStringList(d.Get(networkDeviceTypeSchemaNames["MetroCodes"]).(*schema.Set)) if err != nil { return diag.FromErr(err) } diff --git a/equinix/data_source_network_platform.go b/equinix/data_source_network_platform.go index 41a391280..e387498c1 100644 --- a/equinix/data_source_network_platform.go +++ b/equinix/data_source_network_platform.go @@ -4,6 +4,8 @@ import ( "context" "fmt" + "github.com/equinix/terraform-provider-equinix/internal" + "github.com/equinix/terraform-provider-equinix/internal/config" "github.com/equinix/ne-go" @@ -122,19 +124,19 @@ func dataSourceNetworkDevicePlatformRead(ctx context.Context, d *schema.Resource continue } if v, ok := d.GetOk(networkDevicePlatformSchemaNames["PackageCodes"]); ok { - pkgCodes := expandSetToStringList(v.(*schema.Set)) + pkgCodes := internal.ExpandSetToStringList(v.(*schema.Set)) if !stringsFound(pkgCodes, platform.PackageCodes) { continue } } if v, ok := d.GetOk(networkDevicePlatformSchemaNames["ManagementTypes"]); ok { - mgmtTypes := expandSetToStringList(v.(*schema.Set)) + mgmtTypes := internal.ExpandSetToStringList(v.(*schema.Set)) if !stringsFound(mgmtTypes, platform.ManagementTypes) { continue } } if v, ok := d.GetOk(networkDevicePlatformSchemaNames["LicenseOptions"]); ok { - licOptions := expandSetToStringList(v.(*schema.Set)) + licOptions := internal.ExpandSetToStringList(v.(*schema.Set)) if !stringsFound(licOptions, platform.LicenseOptions) { continue } diff --git a/equinix/equinix_sweeper_test.go b/equinix/equinix_sweeper_test.go index f5e30109d..4e00a99fd 100644 --- a/equinix/equinix_sweeper_test.go +++ b/equinix/equinix_sweeper_test.go @@ -19,20 +19,20 @@ func TestMain(m *testing.M) { } func sharedConfigForRegion(region string) (*config.Config, error) { - endpoint := getFromEnvDefault(endpointEnvVar, config.DefaultBaseURL) - clientToken := getFromEnvDefault(clientTokenEnvVar, "") - clientID := getFromEnvDefault(clientIDEnvVar, "") - clientSecret := getFromEnvDefault(clientSecretEnvVar, "") - clientTimeout := getFromEnvDefault(clientTimeoutEnvVar, strconv.Itoa(config.DefaultTimeout)) + endpoint := getFromEnvDefault(EndpointEnvVar, config.DefaultBaseURL) + clientToken := getFromEnvDefault(ClientTokenEnvVar, "") + clientID := getFromEnvDefault(ClientIDEnvVar, "") + clientSecret := getFromEnvDefault(ClientSecretEnvVar, "") + clientTimeout := getFromEnvDefault(ClientTimeoutEnvVar, strconv.Itoa(config.DefaultTimeout)) clientTimeoutInt, err := strconv.Atoi(clientTimeout) if err != nil { - return nil, fmt.Errorf("cannot convert value of '%s' env variable to int", clientTimeoutEnvVar) + return nil, fmt.Errorf("cannot convert value of '%s' env variable to int", ClientTimeoutEnvVar) } - metalAuthToken := getFromEnvDefault(metalAuthTokenEnvVar, "") + metalAuthToken := getFromEnvDefault(MetalAuthTokenEnvVar, "") if clientToken == "" && (clientID == "" || clientSecret == "") && metalAuthToken == "" { return nil, fmt.Errorf("To run acceptance tests sweeper, one of '%s' or pair '%s' - '%s' must be set for Equinix Fabric and Network Edge, and '%s' for Equinix Metal", - clientTokenEnvVar, clientIDEnvVar, clientSecretEnvVar, metalAuthTokenEnvVar) + ClientTokenEnvVar, ClientIDEnvVar, ClientSecretEnvVar, MetalAuthTokenEnvVar) } return &config.Config{ diff --git a/equinix/fabric_necessary_utilities.go b/equinix/fabric_necessary_utilities.go new file mode 100644 index 000000000..965899109 --- /dev/null +++ b/equinix/fabric_necessary_utilities.go @@ -0,0 +1,49 @@ +package equinix + +import ( + "fmt" + "sort" + + "github.com/hashicorp/go-multierror" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +type setFn = func(d *schema.ResourceData, key string) error + +func expandListToStringList(list []interface{}) []string { + result := make([]string, len(list)) + for i, v := range list { + result[i] = fmt.Sprint(v) + } + return result +} + +func expandListToInt32List(list []interface{}) []int32 { + result := make([]int32, len(list)) + for i, v := range list { + result[i] = int32(v.(int)) + } + return result +} + +func setMap(d *schema.ResourceData, m map[string]interface{}) error { + errs := &multierror.Error{} + for key, v := range m { + var err error + if f, ok := v.(setFn); ok { + err = f(d, key) + } else { + if key == "router" { + d.Set("gateway", v) + } + err = d.Set(key, v) + } + + if err != nil { + errs = multierror.Append(errs, err) + } + } + sort.Sort(errs) + + return errs.ErrorOrNil() +} \ No newline at end of file diff --git a/equinix/helpers_device_test.go b/equinix/helpers_device_test.go index 1f9dc7517..48e87e7d5 100644 --- a/equinix/helpers_device_test.go +++ b/equinix/helpers_device_test.go @@ -8,6 +8,7 @@ import ( "golang.org/x/exp/slices" + "github.com/equinix/terraform-provider-equinix/internal" "github.com/packethost/packngo" ) @@ -152,8 +153,8 @@ func Test_waitUntilReservationProvisionable(t *testing.T) { // timeout * number of tests that reach timeout must be less than 30s (default go test timeout). for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if err := waitUntilReservationProvisionable(context.Background(), tt.args.meta, tt.args.reservationId, tt.args.instanceId, 50*time.Millisecond, 1*time.Second, 50*time.Millisecond); (err != nil) != tt.wantErr { - t.Errorf("waitUntilReservationProvisionable() error = %v, wantErr %v", err, tt.wantErr) + if err := internal.WaitUntilReservationProvisionable(context.Background(), tt.args.meta, tt.args.reservationId, tt.args.instanceId, 50*time.Millisecond, 1*time.Second, 50*time.Millisecond); (err != nil) != tt.wantErr { + t.Errorf("WaitUntilReservationProvisionable() error = %v, wantErr %v", err, tt.wantErr) } }) } diff --git a/equinix/data_source_metal_project_ssh_key.go b/equinix/metal_project_ssh_key/data_source.go similarity index 97% rename from equinix/data_source_metal_project_ssh_key.go rename to equinix/metal_project_ssh_key/data_source.go index e20013c4c..21cdaa49c 100644 --- a/equinix/data_source_metal_project_ssh_key.go +++ b/equinix/metal_project_ssh_key/data_source.go @@ -1,4 +1,4 @@ -package equinix +package metal_project_ssh_key import ( "fmt" @@ -13,7 +13,7 @@ import ( "github.com/packethost/packngo" ) -func dataSourceMetalProjectSSHKey() *schema.Resource { +func DataSource() *schema.Resource { return &schema.Resource{ Read: dataSourceMetalProjectSSHKeyRead, Schema: map[string]*schema.Schema{ diff --git a/equinix/data_source_metal_project_ssh_key_acc_test.go b/equinix/metal_project_ssh_key/data_source_test.go similarity index 88% rename from equinix/data_source_metal_project_ssh_key_acc_test.go rename to equinix/metal_project_ssh_key/data_source_test.go index 45180e237..5e27a13d5 100644 --- a/equinix/data_source_metal_project_ssh_key_acc_test.go +++ b/equinix/metal_project_ssh_key/data_source_test.go @@ -1,4 +1,4 @@ -package equinix +package metal_project_ssh_key_test import ( "fmt" @@ -7,8 +7,12 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/equinix/terraform-provider-equinix/equinix/acceptance" ) +// + func TestAccDataSourceMetalProjectSSHKey_bySearch(t *testing.T) { datasourceName := "data.equinix_metal_project_ssh_key.foobar" keyName := acctest.RandomWithPrefix("tfacc-project-key") @@ -19,10 +23,10 @@ func TestAccDataSourceMetalProjectSSHKey_bySearch(t *testing.T) { } resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, + PreCheck: func() { acceptance.TestAccPreCheckMetal(t) }, + Providers: acceptance.TestAccProviders, PreventPostDestroyRefresh: true, - CheckDestroy: testAccMetalSSHKeyCheckDestroyed, + CheckDestroy: testAccMetalProjectSSHKeyCheckDestroyed, Steps: []resource.TestStep{ { Config: testAccDataSourceMetalProjectSSHKeyConfig_bySearch(keyName, publicKeyMaterial), @@ -58,10 +62,10 @@ func TestAccDataSourceMetalProjectSSHKeyDataSource_yID(t *testing.T) { } resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, + PreCheck: func() { acceptance.TestAccPreCheckMetal(t) }, + Providers: acceptance.TestAccProviders, PreventPostDestroyRefresh: true, - CheckDestroy: testAccMetalSSHKeyCheckDestroyed, + CheckDestroy: testAccMetalProjectSSHKeyCheckDestroyed, Steps: []resource.TestStep{ { Config: testAccDataSourceMetalProjectSSHKeyDataSourceConfig_byID(keyName, publicKeyMaterial), diff --git a/equinix/metal_project_ssh_key/resource.go b/equinix/metal_project_ssh_key/resource.go new file mode 100644 index 000000000..4c1679408 --- /dev/null +++ b/equinix/metal_project_ssh_key/resource.go @@ -0,0 +1,26 @@ +package metal_project_ssh_key + +import ( + "github.com/equinix/terraform-provider-equinix/equinix/metal_ssh_key" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func Resource() *schema.Resource { + pkeySchema := metal_ssh_key.MetalSSHKeyCommonFields() + pkeySchema["project_id"] = &schema.Schema{ + Type: schema.TypeString, + Description: "The ID of parent project", + ForceNew: true, + Required: true, + } + return &schema.Resource{ + Create: metal_ssh_key.ResourceMetalSSHKeyCreate, + Read: metal_ssh_key.ResourceMetalSSHKeyRead, + Update: metal_ssh_key.ResourceMetalSSHKeyUpdate, + Delete: metal_ssh_key.ResourceMetalSSHKeyDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Schema: pkeySchema, + } +} diff --git a/equinix/resource_metal_project_ssh_key_acc_test.go b/equinix/metal_project_ssh_key/resource_test.go similarity index 78% rename from equinix/resource_metal_project_ssh_key_acc_test.go rename to equinix/metal_project_ssh_key/resource_test.go index 40e2ac9e9..331d9fe9d 100644 --- a/equinix/resource_metal_project_ssh_key_acc_test.go +++ b/equinix/metal_project_ssh_key/resource_test.go @@ -1,4 +1,4 @@ -package equinix +package metal_project_ssh_key_test import ( "fmt" @@ -6,6 +6,7 @@ import ( "github.com/equinix/terraform-provider-equinix/internal/config" + "github.com/equinix/terraform-provider-equinix/equinix/acceptance" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -37,7 +38,13 @@ resource "equinix_metal_device" "test" { termination_time = "%s" } -`, confAccMetalDevice_base(preferable_plans, preferable_metros, preferable_os), name, publicSshKey, testDeviceTerminationTime()) +`, acceptance.ConfAccMetalDevice_base( + acceptance.Preferable_plans, + acceptance.Preferable_metros, + acceptance.Preferable_os), + name, + publicSshKey, + acceptance.TestDeviceTerminationTime()) } func TestAccMetalProjectSSHKey_basic(t *testing.T) { @@ -50,15 +57,15 @@ func TestAccMetalProjectSSHKey_basic(t *testing.T) { cfg := testAccMetalProjectSSHKeyConfig_basic(rs, publicKeyMaterial) resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ExternalProviders: testExternalProviders, - Providers: testAccProviders, + PreCheck: func() { acceptance.TestAccPreCheckMetal(t) }, + ExternalProviders: acceptance.TestExternalProviders, + Providers: acceptance.TestAccProviders, CheckDestroy: testAccMetalProjectSSHKeyCheckDestroyed, Steps: []resource.TestStep{ { Config: cfg, Check: resource.ComposeTestCheckFunc( - testAccCheckMetalSSHKeyExists("equinix_metal_project_ssh_key.test", &key), + acceptance.TestAccCheckMetalSSHKeyExists("equinix_metal_project_ssh_key.test", &key), resource.TestCheckResourceAttr( "equinix_metal_project_ssh_key.test", "public_key", publicKeyMaterial), resource.TestCheckResourceAttrPair( @@ -76,7 +83,7 @@ func TestAccMetalProjectSSHKey_basic(t *testing.T) { } func testAccMetalProjectSSHKeyCheckDestroyed(s *terraform.State) error { - client := testAccProvider.Meta().(*config.Config).Metal + client := acceptance.TestAccProvider.Meta().(*config.Config).Metal for _, rs := range s.RootModule().Resources { if rs.Type != "equinix_metal_project_ssh_key" { diff --git a/equinix/resource_metal_ssh_key.go b/equinix/metal_ssh_key/resource.go similarity index 84% rename from equinix/resource_metal_ssh_key.go rename to equinix/metal_ssh_key/resource.go index 2faaba737..e422a8be8 100644 --- a/equinix/resource_metal_ssh_key.go +++ b/equinix/metal_ssh_key/resource.go @@ -1,4 +1,4 @@ -package equinix +package metal_ssh_key import ( "log" @@ -13,7 +13,7 @@ import ( "github.com/packethost/packngo" ) -func metalSSHKeyCommonFields() map[string]*schema.Schema { +func MetalSSHKeyCommonFields() map[string]*schema.Schema { return map[string]*schema.Schema{ "name": { Type: schema.TypeString, @@ -52,21 +52,21 @@ func metalSSHKeyCommonFields() map[string]*schema.Schema { } } -func resourceMetalSSHKey() *schema.Resource { +func Resource() *schema.Resource { return &schema.Resource{ - Create: resourceMetalSSHKeyCreate, - Read: resourceMetalSSHKeyRead, - Update: resourceMetalSSHKeyUpdate, - Delete: resourceMetalSSHKeyDelete, + Create: ResourceMetalSSHKeyCreate, + Read: ResourceMetalSSHKeyRead, + Update: ResourceMetalSSHKeyUpdate, + Delete: ResourceMetalSSHKeyDelete, Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, - Schema: metalSSHKeyCommonFields(), + Schema: MetalSSHKeyCommonFields(), } } -func resourceMetalSSHKeyCreate(d *schema.ResourceData, meta interface{}) error { +func ResourceMetalSSHKeyCreate(d *schema.ResourceData, meta interface{}) error { meta.(*config.Config).AddModuleToMetalUserAgent(d) client := meta.(*config.Config).Metal @@ -87,10 +87,10 @@ func resourceMetalSSHKeyCreate(d *schema.ResourceData, meta interface{}) error { d.SetId(key.ID) - return resourceMetalSSHKeyRead(d, meta) + return ResourceMetalSSHKeyRead(d, meta) } -func resourceMetalSSHKeyRead(d *schema.ResourceData, meta interface{}) error { +func ResourceMetalSSHKeyRead(d *schema.ResourceData, meta interface{}) error { meta.(*config.Config).AddModuleToMetalUserAgent(d) client := meta.(*config.Config).Metal @@ -126,7 +126,7 @@ func resourceMetalSSHKeyRead(d *schema.ResourceData, meta interface{}) error { return nil } -func resourceMetalSSHKeyUpdate(d *schema.ResourceData, meta interface{}) error { +func ResourceMetalSSHKeyUpdate(d *schema.ResourceData, meta interface{}) error { meta.(*config.Config).AddModuleToMetalUserAgent(d) client := meta.(*config.Config).Metal @@ -147,10 +147,10 @@ func resourceMetalSSHKeyUpdate(d *schema.ResourceData, meta interface{}) error { return equinix_errors.FriendlyError(err) } - return resourceMetalSSHKeyRead(d, meta) + return ResourceMetalSSHKeyRead(d, meta) } -func resourceMetalSSHKeyDelete(d *schema.ResourceData, meta interface{}) error { +func ResourceMetalSSHKeyDelete(d *schema.ResourceData, meta interface{}) error { meta.(*config.Config).AddModuleToMetalUserAgent(d) client := meta.(*config.Config).Metal diff --git a/equinix/resource_metal_ssh_key_acc_test.go b/equinix/metal_ssh_key/resource_test.go similarity index 81% rename from equinix/resource_metal_ssh_key_acc_test.go rename to equinix/metal_ssh_key/resource_test.go index 82601cd12..651062421 100644 --- a/equinix/resource_metal_ssh_key_acc_test.go +++ b/equinix/metal_ssh_key/resource_test.go @@ -1,4 +1,4 @@ -package equinix +package metal_ssh_key_test import ( "fmt" @@ -6,6 +6,7 @@ import ( "net/http" "testing" + "github.com/equinix/terraform-provider-equinix/equinix/acceptance" "github.com/equinix/terraform-provider-equinix/internal/config" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" @@ -23,7 +24,7 @@ func init() { func testSweepSSHKeys(region string) error { log.Printf("[DEBUG] Sweeping ssh keys") - config, err := sharedConfigForRegion(region) + config, err := acceptance.GetConfigForNonStandardMetalTest() if err != nil { return fmt.Errorf("[INFO][SWEEPER_LOG] Error getting configuration for sweeping ssh keys: %s", err) } @@ -34,7 +35,7 @@ func testSweepSSHKeys(region string) error { } ids := []string{} for _, k := range sshkeys { - if isSweepableTestResource(k.Label) { + if acceptance.IsSweepableTestResource(k.Label) { ids = append(ids, k.ID) } } @@ -57,15 +58,14 @@ func TestAccMetalSSHKey_basic(t *testing.T) { } resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ExternalProviders: testExternalProviders, - Providers: testAccProviders, - CheckDestroy: testAccMetalSSHKeyCheckDestroyed, + PreCheck: func() { acceptance.TestAccPreCheckMetal(t) }, + Providers: acceptance.TestAccProviders, + CheckDestroy: testAccMetalSSHKeyCheckDestroyed, Steps: []resource.TestStep{ { Config: testAccMetalSSHKeyConfig_basic(rInt, publicKeyMaterial), Check: resource.ComposeTestCheckFunc( - testAccCheckMetalSSHKeyExists("equinix_metal_ssh_key.foobar", &key), + acceptance.TestAccCheckMetalSSHKeyExists("equinix_metal_ssh_key.foobar", &key), resource.TestCheckResourceAttr( "equinix_metal_ssh_key.foobar", "name", fmt.Sprintf("tfacc-user-key-%d", rInt)), resource.TestCheckResourceAttr( @@ -86,10 +86,9 @@ func TestAccMetalSSHKey_projectBasic(t *testing.T) { } resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ExternalProviders: testExternalProviders, - Providers: testAccProviders, - CheckDestroy: testAccMetalSSHKeyCheckDestroyed, + PreCheck: func() { acceptance.TestAccPreCheckMetal(t) }, + Providers: acceptance.TestAccProviders, + CheckDestroy: testAccMetalSSHKeyCheckDestroyed, Steps: []resource.TestStep{ { Config: testAccCheckMetalSSHKeyConfig_projectBasic(rInt, publicKeyMaterial), @@ -113,15 +112,14 @@ func TestAccMetalSSHKey_update(t *testing.T) { } resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ExternalProviders: testExternalProviders, - Providers: testAccProviders, - CheckDestroy: testAccMetalSSHKeyCheckDestroyed, + PreCheck: func() { acceptance.TestAccPreCheckMetal(t) }, + Providers: acceptance.TestAccProviders, + CheckDestroy: testAccMetalSSHKeyCheckDestroyed, Steps: []resource.TestStep{ { Config: testAccMetalSSHKeyConfig_basic(rInt, publicKeyMaterial), Check: resource.ComposeTestCheckFunc( - testAccCheckMetalSSHKeyExists("equinix_metal_ssh_key.foobar", &key), + acceptance.TestAccCheckMetalSSHKeyExists("equinix_metal_ssh_key.foobar", &key), resource.TestCheckResourceAttr( "equinix_metal_ssh_key.foobar", "name", fmt.Sprintf("tfacc-user-key-%d", rInt)), resource.TestCheckResourceAttr( @@ -131,7 +129,7 @@ func TestAccMetalSSHKey_update(t *testing.T) { { Config: testAccMetalSSHKeyConfig_basic(rInt+1, publicKeyMaterial), Check: resource.ComposeTestCheckFunc( - testAccCheckMetalSSHKeyExists("equinix_metal_ssh_key.foobar", &key), + acceptance.TestAccCheckMetalSSHKeyExists("equinix_metal_ssh_key.foobar", &key), resource.TestCheckResourceAttr( "equinix_metal_ssh_key.foobar", "name", fmt.Sprintf("tfacc-user-key-%d", rInt+1)), resource.TestCheckResourceAttr( @@ -148,10 +146,9 @@ func TestAccMetalSSHKey_projectImportBasic(t *testing.T) { t.Fatalf("Cannot generate test SSH key pair: %s", err) } resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ExternalProviders: testExternalProviders, - Providers: testAccProviders, - CheckDestroy: testAccMetalSSHKeyCheckDestroyed, + PreCheck: func() { acceptance.TestAccPreCheckMetal(t) }, + Providers: acceptance.TestAccProviders, + CheckDestroy: testAccMetalSSHKeyCheckDestroyed, Steps: []resource.TestStep{ { Config: testAccCheckMetalSSHKeyConfig_projectBasic(acctest.RandInt(), sshKey), @@ -171,10 +168,9 @@ func TestAccMetalSSHKey_importBasic(t *testing.T) { t.Fatalf("Cannot generate test SSH key pair: %s", err) } resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ExternalProviders: testExternalProviders, - Providers: testAccProviders, - CheckDestroy: testAccMetalSSHKeyCheckDestroyed, + PreCheck: func() { acceptance.TestAccPreCheckMetal(t) }, + Providers: acceptance.TestAccProviders, + CheckDestroy: testAccMetalSSHKeyCheckDestroyed, Steps: []resource.TestStep{ { Config: testAccMetalSSHKeyConfig_basic(acctest.RandInt(), sshKey), @@ -190,7 +186,7 @@ func TestAccMetalSSHKey_importBasic(t *testing.T) { } func testAccMetalSSHKeyCheckDestroyed(s *terraform.State) error { - client := testAccProvider.Meta().(*config.Config).Metal + client := acceptance.TestAccProvider.Meta().(*config.Config).Metal for _, rs := range s.RootModule().Resources { if rs.Type != "equinix_metal_ssh_key" { @@ -214,7 +210,7 @@ func testAccCheckMetalSSHKeyExists(n string, key *packngo.SSHKey) resource.TestC return fmt.Errorf("No Record ID is set") } - client := testAccProvider.Meta().(*config.Config).Metal + client := acceptance.TestAccProvider.Meta().(*config.Config).Metal foundKey, _, err := client.SSHKeys.Get(rs.Primary.ID, nil) if err != nil { diff --git a/equinix/provider.go b/equinix/provider.go index 2ff4a5dcf..7290f0c7c 100644 --- a/equinix/provider.go +++ b/equinix/provider.go @@ -16,10 +16,15 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + + "github.com/equinix/terraform-provider-equinix/internal" + + "github.com/equinix/terraform-provider-equinix/equinix/metal_project_ssh_key" + "github.com/equinix/terraform-provider-equinix/equinix/metal_ssh_key" ) var ( - metalMutexKV = NewMutexKV() + metalMutexKV = internal.NewMutexKV() DeviceNetworkTypes = []string{"layer3", "hybrid", "layer2-individual", "layer2-bonded"} DeviceNetworkTypesHB = []string{"layer3", "hybrid", "hybrid-bonded", "layer2-individual", "layer2-bonded"} NetworkTypeList = strings.Join(DeviceNetworkTypes, ", ") @@ -27,12 +32,12 @@ var ( ) const ( - endpointEnvVar = "EQUINIX_API_ENDPOINT" - clientIDEnvVar = "EQUINIX_API_CLIENTID" - clientSecretEnvVar = "EQUINIX_API_CLIENTSECRET" - clientTokenEnvVar = "EQUINIX_API_TOKEN" - clientTimeoutEnvVar = "EQUINIX_API_TIMEOUT" - metalAuthTokenEnvVar = "METAL_AUTH_TOKEN" + EndpointEnvVar = "EQUINIX_API_ENDPOINT" + ClientIDEnvVar = "EQUINIX_API_CLIENTID" + ClientSecretEnvVar = "EQUINIX_API_CLIENTSECRET" + ClientTokenEnvVar = "EQUINIX_API_TOKEN" + ClientTimeoutEnvVar = "EQUINIX_API_TIMEOUT" + MetalAuthTokenEnvVar = "METAL_AUTH_TOKEN" ) // resourceDataProvider provies interface to schema.ResourceData @@ -51,38 +56,38 @@ func Provider() *schema.Provider { "endpoint": { Type: schema.TypeString, Optional: true, - DefaultFunc: schema.EnvDefaultFunc(endpointEnvVar, config.DefaultBaseURL), + DefaultFunc: schema.EnvDefaultFunc(EndpointEnvVar, config.DefaultBaseURL), ValidateFunc: validation.IsURLWithHTTPorHTTPS, Description: fmt.Sprintf("The Equinix API base URL to point out desired environment. Defaults to %s", config.DefaultBaseURL), }, "client_id": { Type: schema.TypeString, Optional: true, - DefaultFunc: schema.EnvDefaultFunc(clientIDEnvVar, ""), + DefaultFunc: schema.EnvDefaultFunc(ClientIDEnvVar, ""), Description: "API Consumer Key available under My Apps section in developer portal", }, "client_secret": { Type: schema.TypeString, Optional: true, - DefaultFunc: schema.EnvDefaultFunc(clientSecretEnvVar, ""), + DefaultFunc: schema.EnvDefaultFunc(ClientSecretEnvVar, ""), Description: "API Consumer secret available under My Apps section in developer portal", }, "token": { Type: schema.TypeString, Optional: true, - DefaultFunc: schema.EnvDefaultFunc(clientTokenEnvVar, ""), + DefaultFunc: schema.EnvDefaultFunc(ClientTokenEnvVar, ""), Description: "API token from the developer sandbox", }, "auth_token": { Type: schema.TypeString, Optional: true, - DefaultFunc: schema.EnvDefaultFunc(metalAuthTokenEnvVar, ""), + DefaultFunc: schema.EnvDefaultFunc(MetalAuthTokenEnvVar, ""), Description: "The Equinix Metal API auth key for API operations", }, "request_timeout": { Type: schema.TypeInt, Optional: true, - DefaultFunc: schema.EnvDefaultFunc(clientTimeoutEnvVar, config.DefaultTimeout), + DefaultFunc: schema.EnvDefaultFunc(ClientTimeoutEnvVar, config.DefaultTimeout), ValidateFunc: validation.IntAtLeast(1), Description: fmt.Sprintf("The duration of time, in seconds, that the Equinix Platform API Client should wait before canceling an API request. Defaults to %d", config.DefaultTimeout), }, @@ -135,7 +140,7 @@ func Provider() *schema.Provider { "equinix_metal_plans": dataSourceMetalPlans(), "equinix_metal_port": dataSourceMetalPort(), "equinix_metal_project": dataSourceMetalProject(), - "equinix_metal_project_ssh_key": dataSourceMetalProjectSSHKey(), + "equinix_metal_project_ssh_key": metal_project_ssh_key.DataSource(), "equinix_metal_reserved_ip_block": dataSourceMetalReservedIPBlock(), "equinix_metal_spot_market_request": dataSourceMetalSpotMarketRequest(), "equinix_metal_virtual_circuit": dataSourceMetalVirtualCircuit(), @@ -162,10 +167,10 @@ func Provider() *schema.Provider { "equinix_metal_connection": resourceMetalConnection(), "equinix_metal_device": resourceMetalDevice(), "equinix_metal_device_network_type": resourceMetalDeviceNetworkType(), - "equinix_metal_ssh_key": resourceMetalSSHKey(), + "equinix_metal_ssh_key": metal_ssh_key.Resource(), "equinix_metal_organization_member": resourceMetalOrganizationMember(), "equinix_metal_port": resourceMetalPort(), - "equinix_metal_project_ssh_key": resourceMetalProjectSSHKey(), + "equinix_metal_project_ssh_key": metal_project_ssh_key.Resource(), "equinix_metal_project": resourceMetalProject(), "equinix_metal_organization": resourceMetalOrganization(), "equinix_metal_reserved_ip_block": resourceMetalReservedIPBlock(), @@ -192,14 +197,11 @@ func Provider() *schema.Provider { return provider } -type providerMeta struct { - ModuleName string `cty:"module_name"` -} - func configureProvider(ctx context.Context, d *schema.ResourceData, p *schema.Provider) (interface{}, diag.Diagnostics) { mrws := d.Get("max_retry_wait_seconds").(int) rt := d.Get("request_timeout").(int) + meta := config.ProviderMeta{} config := config.Config{ AuthToken: d.Get("auth_token").(string), BaseURL: d.Get("endpoint").(string), @@ -211,7 +213,6 @@ func configureProvider(ctx context.Context, d *schema.ResourceData, p *schema.Pr MaxRetries: d.Get("max_retries").(int), MaxRetryWait: time.Duration(mrws) * time.Second, } - meta := providerMeta{} if err := d.GetProviderMeta(&meta); err != nil { return nil, diag.FromErr(err) @@ -240,35 +241,6 @@ var resourceDefaultTimeouts = &schema.ResourceTimeout{ Default: schema.DefaultTimeout(60 * time.Minute), } -func expandListToStringList(list []interface{}) []string { - result := make([]string, len(list)) - for i, v := range list { - result[i] = fmt.Sprint(v) - } - return result -} - -func expandListToInt32List(list []interface{}) []int32 { - result := make([]int32, len(list)) - for i, v := range list { - result[i] = int32(v.(int)) - } - return result -} - -func expandSetToStringList(set *schema.Set) []string { - list := set.List() - return expandListToStringList(list) -} - -func expandInterfaceMapToStringMap(mapIn map[string]interface{}) map[string]string { - mapOut := make(map[string]string) - for k, v := range mapIn { - mapOut[k] = fmt.Sprintf("%v", v) - } - return mapOut -} - func hasApplicationErrorCode(errors []rest.ApplicationError, code string) bool { for _, err := range errors { if err.Code == code { diff --git a/equinix/provider_test.go b/equinix/provider_test.go index 590bf84ab..b26a1a2c8 100644 --- a/equinix/provider_test.go +++ b/equinix/provider_test.go @@ -403,20 +403,20 @@ func TestProvider_schemaSetToMap(t *testing.T) { func testAccPreCheck(t *testing.T) { var err error - if _, err = getFromEnv(clientTokenEnvVar); err != nil { - _, err = getFromEnv(clientIDEnvVar) + if _, err = getFromEnv(ClientTokenEnvVar); err != nil { + _, err = getFromEnv(ClientIDEnvVar) if err == nil { - _, err = getFromEnv(clientSecretEnvVar) + _, err = getFromEnv(ClientSecretEnvVar) } } if err == nil { - _, err = getFromEnv(metalAuthTokenEnvVar) + _, err = getFromEnv(MetalAuthTokenEnvVar) } if err != nil { t.Fatalf("To run acceptance tests, one of '%s' or pair '%s' - '%s' must be set for Equinix Fabric and Network Edge, and '%s' for Equinix Metal", - clientTokenEnvVar, clientIDEnvVar, clientSecretEnvVar, metalAuthTokenEnvVar) + ClientTokenEnvVar, ClientIDEnvVar, ClientSecretEnvVar, MetalAuthTokenEnvVar) } } diff --git a/equinix/resource_ecx_l2_connection.go b/equinix/resource_ecx_l2_connection.go index 64cd0448f..93a0d77df 100644 --- a/equinix/resource_ecx_l2_connection.go +++ b/equinix/resource_ecx_l2_connection.go @@ -7,6 +7,8 @@ import ( "strings" "time" + "github.com/equinix/terraform-provider-equinix/internal" + "github.com/equinix/terraform-provider-equinix/internal/config" "github.com/equinix/ecx-go/v2" @@ -825,7 +827,7 @@ func createECXL2Connections(d *schema.ResourceData) (*ecx.L2Connection, *ecx.L2C primary.SpeedUnit = ecx.String(v.(string)) } if v, ok := d.GetOk(ecxL2ConnectionSchemaNames["Notifications"]); ok { - primary.Notifications = expandSetToStringList(v.(*schema.Set)) + primary.Notifications = internal.ExpandSetToStringList(v.(*schema.Set)) } if v, ok := d.GetOk(ecxL2ConnectionSchemaNames["PurchaseOrderNumber"]); ok { primary.PurchaseOrderNumber = ecx.String(v.(string)) diff --git a/equinix/resource_ecx_l2_connection_test.go b/equinix/resource_ecx_l2_connection_test.go index 219ee7db7..a7b73a939 100644 --- a/equinix/resource_ecx_l2_connection_test.go +++ b/equinix/resource_ecx_l2_connection_test.go @@ -4,6 +4,8 @@ import ( "fmt" "testing" + "github.com/equinix/terraform-provider-equinix/internal" + "github.com/equinix/ecx-go/v2" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/stretchr/testify/assert" @@ -111,7 +113,7 @@ func TestFabricL2Connection_updateResourceData(t *testing.T) { assert.Equal(t, ecx.StringValue(input.SpeedUnit), d.Get(ecxL2ConnectionSchemaNames["SpeedUnit"]), "SpeedUnit matches") assert.Equal(t, ecx.StringValue(input.Status), d.Get(ecxL2ConnectionSchemaNames["Status"]), "Status matches") assert.Equal(t, ecx.StringValue(input.ProviderStatus), d.Get(ecxL2ConnectionSchemaNames["ProviderStatus"]), "ProviderStatus matches") - assert.Equal(t, input.Notifications, expandSetToStringList(d.Get(ecxL2ConnectionSchemaNames["Notifications"]).(*schema.Set)), "Notifications matches") + assert.Equal(t, input.Notifications, internal.ExpandSetToStringList(d.Get(ecxL2ConnectionSchemaNames["Notifications"]).(*schema.Set)), "Notifications matches") assert.Equal(t, ecx.StringValue(input.PurchaseOrderNumber), d.Get(ecxL2ConnectionSchemaNames["PurchaseOrderNumber"]), "PurchaseOrderNumber matches") assert.Equal(t, ecx.StringValue(input.PortUUID), d.Get(ecxL2ConnectionSchemaNames["PortUUID"]), "PortUUID matches") assert.Equal(t, ecx.StringValue(input.DeviceUUID), d.Get(ecxL2ConnectionSchemaNames["DeviceUUID"]), "DeviceUUID matches") diff --git a/equinix/resource_ecx_l2_serviceprofile.go b/equinix/resource_ecx_l2_serviceprofile.go index 4aa17b8e3..c5bea373d 100644 --- a/equinix/resource_ecx_l2_serviceprofile.go +++ b/equinix/resource_ecx_l2_serviceprofile.go @@ -4,6 +4,8 @@ import ( "context" "fmt" + "github.com/equinix/terraform-provider-equinix/internal" + "github.com/equinix/terraform-provider-equinix/internal/config" "github.com/equinix/ecx-go/v2" @@ -445,13 +447,13 @@ func createECXL2ServiceProfile(d *schema.ResourceData) *ecx.L2ServiceProfile { profile.Name = ecx.String(v.(string)) } if v, ok := d.GetOk(ecxL2ServiceProfileSchemaNames["OnBandwidthThresholdNotification"]); ok { - profile.OnBandwidthThresholdNotification = expandSetToStringList(v.(*schema.Set)) + profile.OnBandwidthThresholdNotification = internal.ExpandSetToStringList(v.(*schema.Set)) } if v, ok := d.GetOk(ecxL2ServiceProfileSchemaNames["OnProfileApprovalRejectNotification"]); ok { - profile.OnProfileApprovalRejectNotification = expandSetToStringList(v.(*schema.Set)) + profile.OnProfileApprovalRejectNotification = internal.ExpandSetToStringList(v.(*schema.Set)) } if v, ok := d.GetOk(ecxL2ServiceProfileSchemaNames["OnVcApprovalRejectionNotification"]); ok { - profile.OnVcApprovalRejectionNotification = expandSetToStringList(v.(*schema.Set)) + profile.OnVcApprovalRejectionNotification = internal.ExpandSetToStringList(v.(*schema.Set)) } if v, ok := d.GetOk(ecxL2ServiceProfileSchemaNames["OverSubscription"]); ok { profile.OverSubscription = ecx.String(v.(string)) @@ -460,7 +462,7 @@ func createECXL2ServiceProfile(d *schema.ResourceData) *ecx.L2ServiceProfile { profile.Private = ecx.Bool(v.(bool)) } if v, ok := d.GetOk(ecxL2ServiceProfileSchemaNames["PrivateUserEmails"]); ok { - profile.PrivateUserEmails = expandSetToStringList(v.(*schema.Set)) + profile.PrivateUserEmails = internal.ExpandSetToStringList(v.(*schema.Set)) } if v, ok := d.GetOk(ecxL2ServiceProfileSchemaNames["RequiredRedundancy"]); ok { profile.RequiredRedundancy = ecx.Bool(v.(bool)) @@ -550,7 +552,7 @@ func updateECXL2ServiceProfileResource(profile *ecx.L2ServiceProfile, d *schema. // API accepts capitalizations of the private user emails and converts it to a lowercase string // If API retuns same emails in lowercase we keep to suppress diff if v, ok := d.GetOk(ecxL2ServiceProfileSchemaNames["PrivateUserEmails"]); ok { - prevPrivateUserEmails := expandSetToStringList(v.(*schema.Set)) + prevPrivateUserEmails := internal.ExpandSetToStringList(v.(*schema.Set)) if slicesMatchCaseInsensitive(prevPrivateUserEmails, profile.PrivateUserEmails) { profile.PrivateUserEmails = prevPrivateUserEmails } diff --git a/equinix/resource_ecx_l2_serviceprofile_test.go b/equinix/resource_ecx_l2_serviceprofile_test.go index 8cb59a4ee..8da560eef 100644 --- a/equinix/resource_ecx_l2_serviceprofile_test.go +++ b/equinix/resource_ecx_l2_serviceprofile_test.go @@ -3,6 +3,8 @@ package equinix import ( "testing" + "github.com/equinix/terraform-provider-equinix/internal" + "github.com/equinix/ecx-go/v2" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/stretchr/testify/assert" @@ -167,12 +169,12 @@ func TestFabricL2ServiceProfile_updateResourceData(t *testing.T) { assert.Equal(t, ecx.BoolValue(input.EquinixManagedPortAndVlan), d.Get(ecxL2ServiceProfileSchemaNames["EquinixManagedPortAndVlan"]), "EquinixManagedPortAndVlan matches") assert.Equal(t, ecx.StringValue(input.IntegrationID), d.Get(ecxL2ServiceProfileSchemaNames["IntegrationID"]), "IntegrationID matches") assert.Equal(t, ecx.StringValue(input.Name), d.Get(ecxL2ServiceProfileSchemaNames["Name"]), "Name matches") - assert.Equal(t, input.OnBandwidthThresholdNotification, expandSetToStringList(d.Get(ecxL2ServiceProfileSchemaNames["OnBandwidthThresholdNotification"]).(*schema.Set)), "OnBandwidthThresholdNotification matches") - assert.Equal(t, input.OnProfileApprovalRejectNotification, expandSetToStringList(d.Get(ecxL2ServiceProfileSchemaNames["OnProfileApprovalRejectNotification"]).(*schema.Set)), "OnProfileApprovalRejectNotification matches") - assert.Equal(t, input.OnVcApprovalRejectionNotification, expandSetToStringList(d.Get(ecxL2ServiceProfileSchemaNames["OnVcApprovalRejectionNotification"]).(*schema.Set)), "OnVcApprovalRejectionNotification matches") + assert.Equal(t, input.OnBandwidthThresholdNotification, internal.ExpandSetToStringList(d.Get(ecxL2ServiceProfileSchemaNames["OnBandwidthThresholdNotification"]).(*schema.Set)), "OnBandwidthThresholdNotification matches") + assert.Equal(t, input.OnProfileApprovalRejectNotification, internal.ExpandSetToStringList(d.Get(ecxL2ServiceProfileSchemaNames["OnProfileApprovalRejectNotification"]).(*schema.Set)), "OnProfileApprovalRejectNotification matches") + assert.Equal(t, input.OnVcApprovalRejectionNotification, internal.ExpandSetToStringList(d.Get(ecxL2ServiceProfileSchemaNames["OnVcApprovalRejectionNotification"]).(*schema.Set)), "OnVcApprovalRejectionNotification matches") assert.Equal(t, ecx.StringValue(input.OverSubscription), d.Get(ecxL2ServiceProfileSchemaNames["OverSubscription"]), "OverSubscription matches") assert.Equal(t, ecx.BoolValue(input.Private), d.Get(ecxL2ServiceProfileSchemaNames["Private"]), "Private matches") - assert.Equal(t, input.PrivateUserEmails, expandSetToStringList(d.Get(ecxL2ServiceProfileSchemaNames["PrivateUserEmails"]).(*schema.Set)), "PrivateUserEmails matches") + assert.Equal(t, input.PrivateUserEmails, internal.ExpandSetToStringList(d.Get(ecxL2ServiceProfileSchemaNames["PrivateUserEmails"]).(*schema.Set)), "PrivateUserEmails matches") assert.Equal(t, ecx.BoolValue(input.RequiredRedundancy), d.Get(ecxL2ServiceProfileSchemaNames["RequiredRedundancy"]), "RequiredRedundancy matches") assert.Equal(t, ecx.BoolValue(input.SpeedFromAPI), d.Get(ecxL2ServiceProfileSchemaNames["SpeedFromAPI"]), "SpeedFromAPI matches") assert.Equal(t, ecx.StringValue(input.TagType), d.Get(ecxL2ServiceProfileSchemaNames["TagType"]), "TagType matches") diff --git a/equinix/resource_metal_device.go b/equinix/resource_metal_device.go index e1a05c7b0..49a3d0fb2 100644 --- a/equinix/resource_metal_device.go +++ b/equinix/resource_metal_device.go @@ -14,6 +14,7 @@ import ( "golang.org/x/exp/slices" + "github.com/equinix/terraform-provider-equinix/internal" "github.com/equinix/terraform-provider-equinix/internal/converters" equinix_errors "github.com/equinix/terraform-provider-equinix/internal/errors" @@ -130,7 +131,7 @@ func resourceMetalDevice() *schema.Resource { Type: schema.TypeList, Description: "A list of IP address types for the device (structure is documented below)", Optional: true, - Elem: ipAddressSchema(), + Elem: internal.IpAddressSchema(), MinItems: 1, }, "plan": { @@ -488,7 +489,7 @@ func resourceMetalDeviceCreate(ctx context.Context, d *schema.ResourceData, meta _, ok := d.GetOk("ip_address") if ok { arr := d.Get("ip_address").([]interface{}) - addressTypesSlice = getNewIPAddressSlice(arr) + addressTypesSlice = internal.GetNewIPAddressSlice(arr) } createRequest := &packngo.DeviceCreateRequest{ @@ -671,7 +672,7 @@ func resourceMetalDeviceRead(ctx context.Context, d *schema.ResourceData, meta i d.Set("deployed_hardware_reservation_id", device.HardwareReservation.GetId()) } - networkType, err := getNetworkType(device) + networkType, err := internal.GetNetworkType(device) if err != nil { return fmt.Errorf("[ERR] Error computing network type for device (%s): %s", d.Id(), err) } @@ -696,14 +697,14 @@ func resourceMetalDeviceRead(ctx context.Context, d *schema.ResourceData, meta i keyIDs = append(keyIDs, path.Base(k.Href)) } d.Set("ssh_key_ids", keyIDs) - networkInfo := getNetworkInfo(device.IpAddresses) + networkInfo := internal.GetNetworkInfo(device.IpAddresses) sort.SliceStable(networkInfo.Networks, func(i, j int) bool { famI := networkInfo.Networks[i]["family"].(int32) famJ := networkInfo.Networks[j]["family"].(int32) pubI := networkInfo.Networks[i]["public"].(bool) pubJ := networkInfo.Networks[j]["public"].(bool) - return getNetworkRank(int(famI), pubI) < getNetworkRank(int(famJ), pubJ) + return internal.GetNetworkRank(int(famI), pubI) < internal.GetNetworkRank(int(famJ), pubJ) }) d.Set("network", networkInfo.Networks) @@ -711,7 +712,7 @@ func resourceMetalDeviceRead(ctx context.Context, d *schema.ResourceData, meta i d.Set("access_private_ipv4", networkInfo.PrivateIPv4) d.Set("access_public_ipv6", networkInfo.PublicIPv6) - ports := getPorts(device.NetworkPorts) + ports := internal.GetPorts(device.NetworkPorts) d.Set("ports", ports) if networkInfo.Host != "" { @@ -855,7 +856,7 @@ func resourceMetalDeviceDelete(ctx context.Context, d *schema.ResourceData, meta // avoid "context: deadline exceeded" timeout := d.Timeout(schema.TimeoutDelete) - 30*time.Second - time.Since(start) - err := waitUntilReservationProvisionable(ctx, client, resId.(string), d.Id(), 10*time.Second, timeout, 3*time.Second) + err := internal.WaitUntilReservationProvisionable(ctx, client, resId.(string), d.Id(), 10*time.Second, timeout, 3*time.Second) if err != nil { return err } @@ -888,7 +889,7 @@ func waitForActiveDevice(ctx context.Context, d *schema.ResourceData, meta inter } // Wait for the device so we can get the networking attributes that show up after a while. - state, err := waitForDeviceAttribute(ctx, d, stateConf) + state, err := internal.WaitForDeviceAttribute(ctx, d, stateConf) if err != nil { d.SetId("") fErr := equinix_errors.FriendlyError(err) diff --git a/equinix/resource_metal_port.go b/equinix/resource_metal_port.go index ed173aff0..97fef11b9 100644 --- a/equinix/resource_metal_port.go +++ b/equinix/resource_metal_port.go @@ -7,6 +7,7 @@ import ( "golang.org/x/exp/slices" + "github.com/equinix/terraform-provider-equinix/internal" equinix_errors "github.com/equinix/terraform-provider-equinix/internal/errors" equinix_schema "github.com/equinix/terraform-provider-equinix/internal/schema" @@ -21,11 +22,6 @@ Race conditions: - Bonding a bond port where underlying eth port has vlans assigned, and those vlans are being removed in the same terraform run */ -var ( - l2Types = []string{"layer2-individual", "layer2-bonded"} - l3Types = []string{"layer3", "hybrid", "hybrid-bonded"} -) - func resourceMetalPort() *schema.Resource { return &schema.Resource{ Timeouts: &schema.ResourceTimeout{ @@ -126,20 +122,20 @@ func resourceMetalPort() *schema.Resource { func resourceMetalPortUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) error { start := time.Now() - cpr, _, err := getClientPortResource(d, meta) + cpr, _, err := internal.GetClientPortResource(d, meta) if err != nil { return equinix_errors.FriendlyError(err) } - for _, f := range [](func(*ClientPortResource) error){ - portSanityChecks, - batchVlans(ctx, start, true), - makeDisbond, - convertToL2, - makeBond, - convertToL3, - batchVlans(ctx, start, false), - updateNativeVlan, + for _, f := range [](func(*internal.ClientPortResource) error){ + internal.PortSanityChecks, + internal.BatchVlans(ctx, start, true), + internal.MakeDisbond, + internal.ConvertToL2, + internal.MakeBond, + internal.ConvertToL3, + internal.BatchVlans(ctx, start, false), + internal.UpdateNativeVlan, } { if err := f(cpr); err != nil { return equinix_errors.FriendlyError(err) @@ -153,7 +149,7 @@ func resourceMetalPortRead(ctx context.Context, d *schema.ResourceData, meta int meta.(*config.Config).AddModuleToMetalUserAgent(d) client := meta.(*config.Config).Metal - port, err := getPortByResourceData(d, client) + port, err := internal.GetPortByResourceData(d, client) if err != nil { if equinix_errors.IsNotFound(err) || equinix_errors.IsForbidden(err) { log.Printf("[WARN] Port (%s) not accessible, removing from state", d.Id()) @@ -172,8 +168,8 @@ func resourceMetalPortRead(ctx context.Context, d *schema.ResourceData, meta int "bonded": port.Data.Bonded, "disbond_supported": port.DisbondOperationSupported, } - l2 := slices.Contains(l2Types, port.NetworkType) - l3 := slices.Contains(l3Types, port.NetworkType) + l2 := slices.Contains(internal.L2Types, port.NetworkType) + l3 := slices.Contains(internal.L3Types, port.NetworkType) if l2 { m["layer2"] = true @@ -208,7 +204,7 @@ func resourceMetalPortDelete(ctx context.Context, d *schema.ResourceData, meta i resetRaw, resetOk := d.GetOk("reset_on_delete") if resetOk && resetRaw.(bool) { start := time.Now() - cpr, resp, err := getClientPortResource(d, meta) + cpr, resp, err := internal.GetClientPortResource(d, meta) if equinix_errors.IgnoreResponseErrors(equinix_errors.HttpForbidden, equinix_errors.HttpNotFound)(resp, err) != nil { return err } @@ -228,17 +224,17 @@ func resourceMetalPortDelete(ctx context.Context, d *schema.ResourceData, meta i }); err != nil { return err } - for _, f := range [](func(*ClientPortResource) error){ - batchVlans(ctx, start, true), - makeBond, - convertToL3, + for _, f := range [](func(*internal.ClientPortResource) error){ + internal.BatchVlans(ctx, start, true), + internal.MakeBond, + internal.ConvertToL3, } { if err := f(cpr); err != nil { return err } } // TODO(displague) error or warn? - if warn := portProperlyDestroyed(cpr.Port); warn != nil { + if warn := internal.PortProperlyDestroyed(cpr.Port); warn != nil { log.Printf("[WARN] %s\n", warn) } } diff --git a/equinix/resource_metal_port_acc_test.go b/equinix/resource_metal_port_acc_test.go index 68e7ce39f..e82979483 100644 --- a/equinix/resource_metal_port_acc_test.go +++ b/equinix/resource_metal_port_acc_test.go @@ -5,6 +5,8 @@ import ( "regexp" "testing" + "github.com/equinix/terraform-provider-equinix/internal" + "github.com/equinix/terraform-provider-equinix/internal/config" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" @@ -373,7 +375,7 @@ func testAccMetalPortDestroyed(s *terraform.State) error { if err != nil { return fmt.Errorf("Error getting port %s during destroy check", pid) } - err = portProperlyDestroyed(p) + err = internal.PortProperlyDestroyed(p) if err != nil { return err } diff --git a/equinix/resource_metal_project_ssh_key.go b/equinix/resource_metal_project_ssh_key.go deleted file mode 100644 index 845d9d0fb..000000000 --- a/equinix/resource_metal_project_ssh_key.go +++ /dev/null @@ -1,25 +0,0 @@ -package equinix - -import ( - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func resourceMetalProjectSSHKey() *schema.Resource { - pkeySchema := metalSSHKeyCommonFields() - pkeySchema["project_id"] = &schema.Schema{ - Type: schema.TypeString, - Description: "The ID of parent project", - ForceNew: true, - Required: true, - } - return &schema.Resource{ - Create: resourceMetalSSHKeyCreate, - Read: resourceMetalSSHKeyRead, - Update: resourceMetalSSHKeyUpdate, - Delete: resourceMetalSSHKeyDelete, - Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, - }, - Schema: pkeySchema, - } -} diff --git a/equinix/resource_metal_spot_market_request.go b/equinix/resource_metal_spot_market_request.go index 5320350c8..8695b9bf5 100644 --- a/equinix/resource_metal_spot_market_request.go +++ b/equinix/resource_metal_spot_market_request.go @@ -56,7 +56,7 @@ func resourceMetalSpotMarketRequest() *schema.Resource { if err != nil { return false } - // suppress diff if the difference between existing and new bid price + // suppress diff if the internal.Difference between existing and new bid price // is less than 2% diffThreshold := .02 priceDiff := oldF / newF diff --git a/equinix/resource_metal_vrf.go b/equinix/resource_metal_vrf.go index 559e9dfe4..fb7261d5d 100644 --- a/equinix/resource_metal_vrf.go +++ b/equinix/resource_metal_vrf.go @@ -4,6 +4,8 @@ import ( "context" "log" + "github.com/equinix/terraform-provider-equinix/internal" + equinix_errors "github.com/equinix/terraform-provider-equinix/internal/errors" equinix_schema "github.com/equinix/terraform-provider-equinix/internal/schema" @@ -69,7 +71,7 @@ func resourceMetalVRFCreate(ctx context.Context, d *schema.ResourceData, meta in Description: d.Get("description").(string), Metro: d.Get("metro").(string), LocalASN: d.Get("local_asn").(int), - IPRanges: expandSetToStringList(d.Get("ip_ranges").(*schema.Set)), + IPRanges: internal.ExpandSetToStringList(d.Get("ip_ranges").(*schema.Set)), } projectId := d.Get("project_id").(string) @@ -101,7 +103,7 @@ func resourceMetalVRFUpdate(ctx context.Context, d *schema.ResourceData, meta in updateRequest.LocalASN = iPtr(d.Get("local_asn").(int)) } if d.HasChange("ip_ranges") { - ipRanges := expandSetToStringList(d.Get("ip_ranges").(*schema.Set)) + ipRanges := internal.ExpandSetToStringList(d.Get("ip_ranges").(*schema.Set)) updateRequest.IPRanges = &ipRanges } diff --git a/equinix/resource_network_device.go b/equinix/resource_network_device.go index b62509623..6b0454ce1 100644 --- a/equinix/resource_network_device.go +++ b/equinix/resource_network_device.go @@ -9,6 +9,8 @@ import ( "path/filepath" "time" + "github.com/equinix/terraform-provider-equinix/internal" + "github.com/equinix/terraform-provider-equinix/internal/config" "github.com/equinix/ne-go" @@ -1072,7 +1074,7 @@ func createNetworkDevices(d *schema.ResourceData) (*ne.Device, *ne.Device) { primary.AccountNumber = ne.String(v.(string)) } if v, ok := d.GetOk(neDeviceSchemaNames["Notifications"]); ok { - primary.Notifications = expandSetToStringList(v.(*schema.Set)) + primary.Notifications = internal.ExpandSetToStringList(v.(*schema.Set)) } if v, ok := d.GetOk(neDeviceSchemaNames["PurchaseOrderNumber"]); ok { primary.PurchaseOrderNumber = ne.String(v.(string)) @@ -1094,7 +1096,7 @@ func createNetworkDevices(d *schema.ResourceData) (*ne.Device, *ne.Device) { } primary.IsSelfManaged = ne.Bool(d.Get(neDeviceSchemaNames["IsSelfManaged"]).(bool)) if v, ok := d.GetOk(neDeviceSchemaNames["VendorConfiguration"]); ok { - primary.VendorConfiguration = expandInterfaceMapToStringMap(v.(map[string]interface{})) + primary.VendorConfiguration = internal.ExpandInterfaceMapToStringMap(v.(map[string]interface{})) } if v, ok := d.GetOk(neDeviceSchemaNames["WanInterfaceId"]); ok { primary.WanInterfaceId = ne.String(v.(string)) @@ -1318,7 +1320,7 @@ func expandNetworkDeviceSecondary(devices []interface{}) *ne.Device { transformed.AccountNumber = ne.String(v.(string)) } if v, ok := device[neDeviceSchemaNames["Notifications"]]; ok { - transformed.Notifications = expandSetToStringList(v.(*schema.Set)) + transformed.Notifications = internal.ExpandSetToStringList(v.(*schema.Set)) } if v, ok := device[neDeviceSchemaNames["AdditionalBandwidth"]]; ok && !isEmpty(v) { transformed.AdditionalBandwidth = ne.Int(v.(int)) @@ -1327,7 +1329,7 @@ func expandNetworkDeviceSecondary(devices []interface{}) *ne.Device { transformed.WanInterfaceId = ne.String(v.(string)) } if v, ok := device[neDeviceSchemaNames["VendorConfiguration"]]; ok { - transformed.VendorConfiguration = expandInterfaceMapToStringMap(v.(map[string]interface{})) + transformed.VendorConfiguration = internal.ExpandInterfaceMapToStringMap(v.(map[string]interface{})) } if v, ok := device[neDeviceSchemaNames["UserPublicKey"]]; ok { userKeys := expandNetworkDeviceUserKeys(v.(*schema.Set)) @@ -1498,7 +1500,7 @@ func fillNetworkDeviceUpdateRequest(updateReq ne.DeviceUpdateRequest, changes ma case neDeviceSchemaNames["TermLength"]: updateReq.WithTermLength(changeValue.(int)) case neDeviceSchemaNames["Notifications"]: - updateReq.WithNotifications(expandSetToStringList(changeValue.(*schema.Set))) + updateReq.WithNotifications(internal.ExpandSetToStringList(changeValue.(*schema.Set))) case neDeviceSchemaNames["AdditionalBandwidth"]: updateReq.WithAdditionalBandwidth(changeValue.(int)) case neDeviceSchemaNames["ACLTemplateUUID"]: diff --git a/equinix/resource_network_device_test.go b/equinix/resource_network_device_test.go index 60db2674a..362214b24 100644 --- a/equinix/resource_network_device_test.go +++ b/equinix/resource_network_device_test.go @@ -8,6 +8,8 @@ import ( "testing" "time" + "github.com/equinix/terraform-provider-equinix/internal" + "github.com/equinix/ne-go" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/stretchr/testify/assert" @@ -143,7 +145,7 @@ func TestNetworkDevice_updateResourceData(t *testing.T) { assert.Empty(t, d.Get(neDeviceSchemaNames["LicenseToken"]), "LicenseToken is empty") assert.Equal(t, ne.StringValue(inputPrimary.ACLTemplateUUID), d.Get(neDeviceSchemaNames["ACLTemplateUUID"]), "ACLTemplateUUID matches") assert.Equal(t, ne.StringValue(inputPrimary.AccountNumber), d.Get(neDeviceSchemaNames["AccountNumber"]), "AccountNumber matches") - assert.Equal(t, inputPrimary.Notifications, expandSetToStringList(d.Get(neDeviceSchemaNames["Notifications"]).(*schema.Set)), "Notifications matches") + assert.Equal(t, inputPrimary.Notifications, internal.ExpandSetToStringList(d.Get(neDeviceSchemaNames["Notifications"]).(*schema.Set)), "Notifications matches") assert.Equal(t, ne.StringValue(inputPrimary.PurchaseOrderNumber), d.Get(neDeviceSchemaNames["PurchaseOrderNumber"]), "PurchaseOrderNumber matches") assert.Equal(t, ne.IntValue(inputPrimary.TermLength), d.Get(neDeviceSchemaNames["TermLength"]), "TermLength matches") assert.Equal(t, ne.IntValue(inputPrimary.AdditionalBandwidth), d.Get(neDeviceSchemaNames["AdditionalBandwidth"]), "AdditionalBandwidth matches") @@ -152,7 +154,7 @@ func TestNetworkDevice_updateResourceData(t *testing.T) { assert.Empty(t, d.Get(neDeviceSchemaNames["WanInterfaceId"]), "Wan Interface Id is empty") assert.Equal(t, ne.IntValue(inputPrimary.CoreCount), d.Get(neDeviceSchemaNames["CoreCount"]), "CoreCount matches") assert.Equal(t, ne.BoolValue(inputPrimary.IsSelfManaged), d.Get(neDeviceSchemaNames["IsSelfManaged"]), "IsSelfManaged matches") - assert.Equal(t, inputPrimary.VendorConfiguration, expandInterfaceMapToStringMap(d.Get(neDeviceSchemaNames["VendorConfiguration"]).(map[string]interface{})), "VendorConfiguration matches") + assert.Equal(t, inputPrimary.VendorConfiguration, internal.ExpandInterfaceMapToStringMap(d.Get(neDeviceSchemaNames["VendorConfiguration"]).(map[string]interface{})), "VendorConfiguration matches") assert.Equal(t, inputPrimary.UserPublicKey, expandNetworkDeviceUserKeys(d.Get(neDeviceSchemaNames["UserPublicKey"]).(*schema.Set))[0], "UserPublicKey matches") assert.Equal(t, ne.IntValue(inputPrimary.ASN), d.Get(neDeviceSchemaNames["ASN"]), "ASN matches") assert.Equal(t, ne.StringValue(inputPrimary.ZoneCode), d.Get(neDeviceSchemaNames["ZoneCode"]), "ZoneCode matches") @@ -296,7 +298,7 @@ func TestNetworkDevice_expandSecondary(t *testing.T) { LicenseFile: ne.String(input[0].(map[string]interface{})[neDeviceSchemaNames["LicenseFile"]].(string)), ACLTemplateUUID: ne.String(input[0].(map[string]interface{})[neDeviceSchemaNames["ACLTemplateUUID"]].(string)), AccountNumber: ne.String(input[0].(map[string]interface{})[neDeviceSchemaNames["AccountNumber"]].(string)), - Notifications: expandSetToStringList(input[0].(map[string]interface{})[neDeviceSchemaNames["Notifications"]].(*schema.Set)), + Notifications: internal.ExpandSetToStringList(input[0].(map[string]interface{})[neDeviceSchemaNames["Notifications"]].(*schema.Set)), AdditionalBandwidth: ne.Int(input[0].(map[string]interface{})[neDeviceSchemaNames["AdditionalBandwidth"]].(int)), VendorConfiguration: map[string]string{ "key": "value", diff --git a/equinix/resource_network_ssh_user.go b/equinix/resource_network_ssh_user.go index 0c718cd1f..571501da4 100644 --- a/equinix/resource_network_ssh_user.go +++ b/equinix/resource_network_ssh_user.go @@ -4,6 +4,8 @@ import ( "context" "fmt" + "github.com/equinix/terraform-provider-equinix/internal" + "github.com/equinix/terraform-provider-equinix/internal/config" "github.com/equinix/ne-go" @@ -127,8 +129,8 @@ func resourceNetworkSSHUserUpdate(ctx context.Context, d *schema.ResourceData, m } if d.HasChange(networkSSHUserSchemaNames["DeviceUUIDs"]) { a, b := d.GetChange(networkSSHUserSchemaNames["DeviceUUIDs"]) - aList := expandSetToStringList(a.(*schema.Set)) - bList := expandSetToStringList(b.(*schema.Set)) + aList := internal.ExpandSetToStringList(a.(*schema.Set)) + bList := internal.ExpandSetToStringList(b.(*schema.Set)) updateReq.WithDeviceChange(aList, bList) } if err := updateReq.Execute(); err != nil { @@ -160,7 +162,7 @@ func createNetworkSSHUser(d *schema.ResourceData) ne.SSHUser { user.Password = ne.String(v.(string)) } if v, ok := d.GetOk(networkSSHUserSchemaNames["DeviceUUIDs"]); ok { - user.DeviceUUIDs = expandSetToStringList(v.(*schema.Set)) + user.DeviceUUIDs = internal.ExpandSetToStringList(v.(*schema.Set)) } return user } diff --git a/equinix/resource_network_ssh_user_test.go b/equinix/resource_network_ssh_user_test.go index 9145ccefe..44e92264b 100644 --- a/equinix/resource_network_ssh_user_test.go +++ b/equinix/resource_network_ssh_user_test.go @@ -3,6 +3,8 @@ package equinix import ( "testing" + "github.com/equinix/terraform-provider-equinix/internal" + "github.com/equinix/ne-go" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/stretchr/testify/assert" @@ -44,5 +46,5 @@ func TestNetworkSSHUser_updateResourceData(t *testing.T) { assert.Nil(t, err, "Update of resource data does not return error") assert.Equal(t, ne.StringValue(input.Username), d.Get(networkSSHUserSchemaNames["Username"]), "Username matches") assert.Equal(t, ne.StringValue(input.Password), d.Get(networkSSHUserSchemaNames["Password"]), "Password matches") - assert.Equal(t, input.DeviceUUIDs, expandSetToStringList(d.Get(networkSSHUserSchemaNames["DeviceUUIDs"]).(*schema.Set)), "DeviceUUIDs matches") + assert.Equal(t, input.DeviceUUIDs, internal.ExpandSetToStringList(d.Get(networkSSHUserSchemaNames["DeviceUUIDs"]).(*schema.Set)), "DeviceUUIDs matches") } diff --git a/internal/config/config.go b/internal/config/config.go index f16ddc7ae..d5d116b62 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -28,11 +28,6 @@ import ( xoauth2 "golang.org/x/oauth2" ) -var ( - UuidRE = regexp.MustCompile("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$") - IpAddressTypes = []string{"public_ipv4", "private_ipv4", "public_ipv6"} -) - type ProviderMeta struct { ModuleName string `cty:"module_name"` } diff --git a/equinix/helpers_device.go b/internal/helpers_device.go similarity index 81% rename from equinix/helpers_device.go rename to internal/helpers_device.go index 028ce803b..ff59a67b4 100644 --- a/equinix/helpers_device.go +++ b/internal/helpers_device.go @@ -1,4 +1,4 @@ -package equinix +package internal import ( "context" @@ -7,6 +7,7 @@ import ( "fmt" "log" "path" + "regexp" "sort" "strings" "sync" @@ -31,8 +32,10 @@ const ( ) var ( - wgMap = map[string]*sync.WaitGroup{} - wgMutex = sync.Mutex{} + UuidRE = regexp.MustCompile("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$") + IpAddressTypes = []string{"public_ipv4", "private_ipv4", "public_ipv6"} + wgMap = map[string]*sync.WaitGroup{} + wgMutex = sync.Mutex{} ) func ifToIPCreateRequest(m interface{}) packngo.IPAddressCreateRequest { @@ -55,7 +58,7 @@ func ifToIPCreateRequest(m interface{}) packngo.IPAddressCreateRequest { return iacr } -func getNewIPAddressSlice(arr []interface{}) []packngo.IPAddressCreateRequest { +func GetNewIPAddressSlice(arr []interface{}) []packngo.IPAddressCreateRequest { addressTypesSlice := make([]packngo.IPAddressCreateRequest, len(arr)) for i, m := range arr { @@ -73,7 +76,7 @@ type NetworkInfo struct { PrivateIPv4 string } -func getNetworkInfo(ips []metalv1.IPAssignment) NetworkInfo { +func GetNetworkInfo(ips []metalv1.IPAssignment) NetworkInfo { ni := NetworkInfo{Networks: make([]map[string]interface{}, 0, 1)} for _, ip := range ips { network := map[string]interface{}{ @@ -103,7 +106,7 @@ func getNetworkInfo(ips []metalv1.IPAssignment) NetworkInfo { return ni } -func getNetworkType(device *metalv1.Device) (*string, error) { +func GetNetworkType(device *metalv1.Device) (*string, error) { pgDevice := packngo.Device{} res, err := device.MarshalJSON() if err == nil { @@ -115,7 +118,7 @@ func getNetworkType(device *metalv1.Device) (*string, error) { return nil, err } -func getNetworkRank(family int, public bool) int { +func GetNetworkRank(family int, public bool) int { switch { case family == 4 && public: return 0 @@ -127,15 +130,15 @@ func getNetworkRank(family int, public bool) int { return 3 } -func getPorts(ps []metalv1.Port) []map[string]interface{} { +func GetPorts(ps []metalv1.Port) []map[string]interface{} { ret := make([]map[string]interface{}, 0, 1) for _, p := range ps { port := map[string]interface{}{ - "name": p.GetName(), - "id": p.GetId(), - "type": p.GetType(), - "mac": p.Data.GetMac(), - "bonded": p.Data.GetBonded(), + "name": p.Name, + "id": p.Id, + "type": p.Type, + "mac": p.Data.Mac, + "bonded": p.Data.Bonded, } ret = append(ret, port) } @@ -163,7 +166,7 @@ func hwReservationStateRefreshFunc(client *packngo.Client, reservationId, instan } } -func waitUntilReservationProvisionable(ctx context.Context, client *packngo.Client, reservationId, instanceId string, delay, timeout, minTimeout time.Duration) error { +func WaitUntilReservationProvisionable(ctx context.Context, client *packngo.Client, reservationId, instanceId string, delay, timeout, minTimeout time.Duration) error { stateConf := &retry.StateChangeConf{ Pending: []string{deprovisioning}, Target: []string{provisionable, reprovisioned}, @@ -187,7 +190,7 @@ func getWaitForDeviceLock(deviceID string) *sync.WaitGroup { return wg } -func waitForDeviceAttribute(ctx context.Context, d *schema.ResourceData, stateConf *retry.StateChangeConf) (string, error) { +func WaitForDeviceAttribute(ctx context.Context, d *schema.ResourceData, stateConf *retry.StateChangeConf) (string, error) { wg := getWaitForDeviceLock(d.Id()) wg.Wait() @@ -214,14 +217,14 @@ func waitForDeviceAttribute(ctx context.Context, d *schema.ResourceData, stateCo return "", err } -func ipAddressSchema() *schema.Resource { +func IpAddressSchema() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ "type": { Type: schema.TypeString, Required: true, - ValidateFunc: validation.StringInSlice(ipAddressTypes, false), - Description: fmt.Sprintf("one of %s", strings.Join(ipAddressTypes, ",")), + ValidateFunc: validation.StringInSlice(IpAddressTypes, false), + Description: fmt.Sprintf("one of %s", strings.Join(IpAddressTypes, ",")), }, "cidr": { Type: schema.TypeInt, @@ -235,27 +238,27 @@ func ipAddressSchema() *schema.Resource { MinItems: 1, Elem: &schema.Schema{ Type: schema.TypeString, - ValidateFunc: validation.StringMatch(uuidRE, "must be a valid UUID"), + ValidateFunc: validation.StringMatch(UuidRE, "must be a valid UUID"), }, }, }, } } -func getDeviceMap(device metalv1.Device) map[string]interface{} { - networkInfo := getNetworkInfo(device.IpAddresses) +func GetDeviceMap(device metalv1.Device) map[string]interface{} { + networkInfo := GetNetworkInfo(device.IpAddresses) sort.SliceStable(networkInfo.Networks, func(i, j int) bool { - famI := int(networkInfo.Networks[i]["family"].(int32)) - famJ := int(networkInfo.Networks[j]["family"].(int32)) - pubI := networkInfo.Networks[i]["public"].(bool) - pubJ := networkInfo.Networks[j]["public"].(bool) - return getNetworkRank(famI, pubI) < getNetworkRank(famJ, pubJ) + famI := int(*networkInfo.Networks[i]["family"].(*int32)) + famJ := int(*networkInfo.Networks[j]["family"].(*int32)) + pubI := *networkInfo.Networks[i]["public"].(*bool) + pubJ := *networkInfo.Networks[j]["public"].(*bool) + return GetNetworkRank(famI, pubI) < GetNetworkRank(famJ, pubJ) }) keyIDs := []string{} for _, k := range device.SshKeys { keyIDs = append(keyIDs, path.Base(k.GetHref())) } - ports := getPorts(device.NetworkPorts) + ports := GetPorts(device.NetworkPorts) return map[string]interface{}{ "hostname": device.GetHostname(), diff --git a/equinix/mutexkv.go b/internal/mutexkv.go similarity index 98% rename from equinix/mutexkv.go rename to internal/mutexkv.go index 29bc015a9..1d2f738cc 100644 --- a/equinix/mutexkv.go +++ b/internal/mutexkv.go @@ -1,4 +1,4 @@ -package equinix +package internal import ( "log" diff --git a/equinix/port_helpers.go b/internal/port_helpers.go similarity index 92% rename from equinix/port_helpers.go rename to internal/port_helpers.go index 29faa3764..53d85a969 100644 --- a/equinix/port_helpers.go +++ b/internal/port_helpers.go @@ -1,4 +1,4 @@ -package equinix +package internal import ( "context" @@ -18,13 +18,18 @@ import ( "github.com/pkg/errors" ) +var ( + L2Types = []string{"layer2-individual", "layer2-bonded"} + L3Types = []string{"layer3", "hybrid", "hybrid-bonded"} +) + type ClientPortResource struct { Client *packngo.Client Port *packngo.Port Resource *schema.ResourceData } -func getClientPortResource(d *schema.ResourceData, meta interface{}) (*ClientPortResource, *packngo.Response, error) { +func GetClientPortResource(d *schema.ResourceData, meta interface{}) (*ClientPortResource, *packngo.Response, error) { meta.(*config.Config).AddModuleToMetalUserAgent(d) client := meta.(*config.Config).Metal @@ -47,7 +52,7 @@ func getClientPortResource(d *schema.ResourceData, meta interface{}) (*ClientPor return cpr, resp, nil } -func getPortByResourceData(d *schema.ResourceData, client *packngo.Client) (*packngo.Port, error) { +func GetPortByResourceData(d *schema.ResourceData, client *packngo.Client) (*packngo.Port, error) { portId, portIdOk := d.GetOk("port_id") resourceId := d.Id() @@ -133,7 +138,7 @@ func specifiedVlanIds(d *schema.ResourceData) []string { return []string{} } -func batchVlans(ctx context.Context, start time.Time, removeOnly bool) func(*ClientPortResource) error { +func BatchVlans(ctx context.Context, start time.Time, removeOnly bool) func(*ClientPortResource) error { return func(cpr *ClientPortResource) error { var vlansToAssign []string var currentNative string @@ -214,7 +219,7 @@ func createAndWaitForBatch(ctx context.Context, start time.Time, cpr *ClientPort return nil } -func updateNativeVlan(cpr *ClientPortResource) error { +func UpdateNativeVlan(cpr *ClientPortResource) error { currentNative := getCurrentNative(cpr.Port) specifiedNative := getSpecifiedNative(cpr.Resource) @@ -265,17 +270,17 @@ func processBondAction(cpr *ClientPortResource, actionIsBond bool) error { return nil } -func makeBond(cpr *ClientPortResource) error { +func MakeBond(cpr *ClientPortResource) error { return processBondAction(cpr, true) } -func makeDisbond(cpr *ClientPortResource) error { +func MakeDisbond(cpr *ClientPortResource) error { return processBondAction(cpr, false) } -func convertToL2(cpr *ClientPortResource) error { +func ConvertToL2(cpr *ClientPortResource) error { l2, l2Ok := cpr.Resource.GetOkExists("layer2") - isLayer2 := slices.Contains(l2Types, cpr.Port.NetworkType) + isLayer2 := slices.Contains(L2Types, cpr.Port.NetworkType) if l2Ok && l2.(bool) && !isLayer2 { port, _, err := cpr.Client.Ports.ConvertToLayerTwo(cpr.Port.ID) @@ -287,9 +292,9 @@ func convertToL2(cpr *ClientPortResource) error { return nil } -func convertToL3(cpr *ClientPortResource) error { +func ConvertToL3(cpr *ClientPortResource) error { l2, l2Ok := cpr.Resource.GetOkExists("layer2") - isLayer2 := slices.Contains(l2Types, cpr.Port.NetworkType) + isLayer2 := slices.Contains(L2Types, cpr.Port.NetworkType) if l2Ok && !l2.(bool) && isLayer2 { ips := []packngo.AddressRequest{ @@ -306,7 +311,7 @@ func convertToL3(cpr *ClientPortResource) error { return nil } -func portSanityChecks(cpr *ClientPortResource) error { +func PortSanityChecks(cpr *ClientPortResource) error { isBondPort := cpr.Port.Type == "NetworkBondPort" // Constraint: Only bond ports have layer2 mode @@ -342,7 +347,7 @@ func portSanityChecks(cpr *ClientPortResource) error { return nil } -func portProperlyDestroyed(port *packngo.Port) error { +func PortProperlyDestroyed(port *packngo.Port) error { var errs []string if !port.Data.Bonded { errs = append(errs, fmt.Sprintf("port %s wasn't bonded after equinix_metal_port destroy;", port.ID)) diff --git a/internal/utils.go b/internal/utils.go new file mode 100644 index 000000000..50afc4409 --- /dev/null +++ b/internal/utils.go @@ -0,0 +1,28 @@ +package internal + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func expandListToStringList(list []interface{}) []string { + result := make([]string, len(list)) + for i, v := range list { + result[i] = fmt.Sprint(v) + } + return result +} + +func ExpandSetToStringList(set *schema.Set) []string { + list := set.List() + return expandListToStringList(list) +} + +func ExpandInterfaceMapToStringMap(mapIn map[string]interface{}) map[string]string { + mapOut := make(map[string]string) + for k, v := range mapIn { + mapOut[k] = fmt.Sprintf("%v", v) + } + return mapOut +}