Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: move device resource and datasource to separate package #707

Merged
merged 1 commit into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 120 additions & 0 deletions equinix/common_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package equinix

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"}
displague marked this conversation as resolved.
Show resolved Hide resolved
preferable_metros = []string{"ch", "ny", "sv", "ty", "am"}
preferable_os = []string{"ubuntu_20_04"}
)

// Deprecated: use the identical TestDeviceTerminationTime from internal/acceptance instead
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(`
displague marked this conversation as resolved.
Show resolved Hide resolved
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[:], `","`)))
}
28 changes: 23 additions & 5 deletions equinix/data_source_metal_device_bgp_neighbors_acc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import (
)

func TestAccDataSourceMetalDeviceBgpNeighbors(t *testing.T) {
projectName := fmt.Sprintf("ds-device-%s", acctest.RandString(10))
projSuffix := fmt.Sprintf("ds-device-%s", acctest.RandString(10))

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ExternalProviders: testExternalProviders,
ProtoV5ProviderFactories: testAccProtoV5ProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccDataSourceMetalDeviceBgpNeighborsConfig(projectName),
Config: testAccDataSourceMetalDeviceBgpNeighborsConfig(projSuffix),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet(
"data.equinix_metal_device_bgp_neighbors.test", "bgp_neighbors.#"),
Expand All @@ -27,16 +27,34 @@ func TestAccDataSourceMetalDeviceBgpNeighbors(t *testing.T) {
})
}

func testAccDataSourceMetalDeviceBgpNeighborsConfig(projectName string) string {
func testAccDataSourceMetalDeviceBgpNeighborsConfig(projSuffix string) string {
return fmt.Sprintf(`
%s

resource "equinix_metal_project" "test" {
name = "tfacc-project-%s"
}

resource "equinix_metal_device" "test" {
hostname = "tfacc-test-device"
plan = local.plan
metro = local.metro
operating_system = local.os
billing_cycle = "hourly"
project_id = "${equinix_metal_project.test.id}"
termination_time = "%s"
}

data "equinix_metal_device" "test" {
project_id = equinix_metal_project.test.id
hostname = equinix_metal_device.test.hostname
}

data "equinix_metal_device_bgp_neighbors" "test" {
device_id = equinix_metal_device.test.id
}

output "bgp_neighbors_listing" {
value = data.equinix_metal_device_bgp_neighbors.test.bgp_neighbors
}
`, testDataSourceMetalDeviceConfig_basic(projectName))
}`, confAccMetalDevice_base(preferable_plans, preferable_metros, preferable_os), projSuffix, testDeviceTerminationTime())
}
7 changes: 4 additions & 3 deletions equinix/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
fabric_connection "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/connection"
fabric_market_place_subscription "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/marketplace"
fabric_network "github.com/equinix/terraform-provider-equinix/internal/resources/fabric/network"
metal_device "github.com/equinix/terraform-provider-equinix/internal/resources/metal/device"
metal_port "github.com/equinix/terraform-provider-equinix/internal/resources/metal/port"
"github.com/equinix/terraform-provider-equinix/internal/resources/metal/virtual_circuit"
"github.com/equinix/terraform-provider-equinix/internal/resources/metal/vrf"
Expand Down Expand Up @@ -104,8 +105,8 @@ func Provider() *schema.Provider {
"equinix_metal_precreated_ip_block": dataSourceMetalPreCreatedIPBlock(),
"equinix_metal_operating_system": dataSourceOperatingSystem(),
"equinix_metal_spot_market_price": dataSourceSpotMarketPrice(),
"equinix_metal_device": dataSourceMetalDevice(),
"equinix_metal_devices": dataSourceMetalDevices(),
"equinix_metal_device": metal_device.DataSource(),
"equinix_metal_devices": metal_device.ListDataSource(),
"equinix_metal_device_bgp_neighbors": dataSourceMetalDeviceBGPNeighbors(),
"equinix_metal_plans": dataSourceMetalPlans(),
"equinix_metal_port": metal_port.DataSource(),
Expand All @@ -129,7 +130,7 @@ func Provider() *schema.Provider {
"equinix_network_file": resourceNetworkFile(),
"equinix_metal_user_api_key": resourceMetalUserAPIKey(),
"equinix_metal_project_api_key": resourceMetalProjectAPIKey(),
"equinix_metal_device": resourceMetalDevice(),
"equinix_metal_device": metal_device.Resource(),
"equinix_metal_device_network_type": resourceMetalDeviceNetworkType(),
"equinix_metal_port": metal_port.Resource(),
"equinix_metal_reserved_ip_block": resourceMetalReservedIPBlock(),
Expand Down
10 changes: 6 additions & 4 deletions equinix/resource_metal_spot_market_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"log"
"reflect"
"regexp"
"sort"
"strconv"
"time"
Expand All @@ -22,6 +23,10 @@ import (
"github.com/packethost/packngo"
)

var (
matchIPXEScript = regexp.MustCompile(`(?i)^#![i]?pxe`)
)

func resourceMetalSpotMarketRequest() *schema.Resource {
return &schema.Resource{
CreateContext: resourceMetalSpotMarketRequestCreate,
Expand Down Expand Up @@ -62,10 +67,7 @@ func resourceMetalSpotMarketRequest() *schema.Resource {
diffThreshold := .02
priceDiff := oldF / newF

if diffThreshold < priceDiff {
return true
}
return false
return diffThreshold < priceDiff
},
},
"facilities": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package equinix
package device

import (
"context"
"encoding/json"
"errors"
"fmt"
"path"
"sort"
Expand All @@ -17,7 +18,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure"
)

func dataSourceMetalDevice() *schema.Resource {
func DataSource() *schema.Resource {
return &schema.Resource{
Description: `The datasource can be used to fetch a single device.

Expand Down Expand Up @@ -253,21 +254,23 @@ func dataSourceMetalDeviceRead(ctx context.Context, d *schema.ResourceData, meta
}
}

d.Set("hostname", device.GetHostname())
d.Set("project_id", device.Project.GetId())
d.Set("device_id", device.GetId())
d.Set("plan", device.Plan.Slug)
d.Set("facility", device.Facility.Code)
var errs []error

errs = append(errs, d.Set("hostname", device.GetHostname()))
errs = append(errs, d.Set("project_id", device.Project.GetId()))
errs = append(errs, d.Set("device_id", device.GetId()))
errs = append(errs, d.Set("plan", device.Plan.Slug))
errs = append(errs, d.Set("facility", device.Facility.Code))
if device.Metro != nil {
d.Set("metro", strings.ToLower(device.Metro.GetCode()))
errs = append(errs, d.Set("metro", strings.ToLower(device.Metro.GetCode())))
}
d.Set("operating_system", device.OperatingSystem.GetSlug())
d.Set("state", device.GetState())
d.Set("billing_cycle", device.GetBillingCycle())
d.Set("ipxe_script_url", device.GetIpxeScriptUrl())
d.Set("always_pxe", device.GetAlwaysPxe())
d.Set("root_password", device.GetRootPassword())
d.Set("sos_hostname", device.GetSos())
errs = append(errs, d.Set("operating_system", device.OperatingSystem.GetSlug()))
errs = append(errs, d.Set("state", device.GetState()))
errs = append(errs, d.Set("billing_cycle", device.GetBillingCycle()))
errs = append(errs, d.Set("ipxe_script_url", device.GetIpxeScriptUrl()))
errs = append(errs, d.Set("always_pxe", device.GetAlwaysPxe()))
errs = append(errs, d.Set("root_password", device.GetRootPassword()))
errs = append(errs, d.Set("sos_hostname", device.GetSos()))

if device.Storage != nil {
rawStorageBytes, err := json.Marshal(device.Storage)
Expand All @@ -279,26 +282,26 @@ func dataSourceMetalDeviceRead(ctx context.Context, d *schema.ResourceData, meta
if err != nil {
return diag.Errorf("[ERR] Error normalizing storage JSON string for device (%s): %s", d.Id(), err)
}
d.Set("storage", storageString)
errs = append(errs, d.Set("storage", storageString))
}

if device.HardwareReservation != nil {
d.Set("hardware_reservation_id", device.HardwareReservation.GetId())
errs = append(errs, d.Set("hardware_reservation_id", device.HardwareReservation.GetId()))
}
networkType, err := getNetworkType(device)
if err != nil {
return diag.Errorf("[ERR] Error computing network type for device (%s): %s", d.Id(), err)
}

d.Set("network_type", networkType)
errs = append(errs, d.Set("network_type", networkType))

d.Set("tags", device.Tags)
errs = append(errs, d.Set("tags", device.Tags))

keyIDs := []string{}
for _, k := range device.SshKeys {
keyIDs = append(keyIDs, path.Base(k.Href))
}
d.Set("ssh_key_ids", keyIDs)
errs = append(errs, d.Set("ssh_key_ids", keyIDs))
networkInfo := getNetworkInfo(device.IpAddresses)

sort.SliceStable(networkInfo.Networks, func(i, j int) bool {
Expand All @@ -309,15 +312,21 @@ func dataSourceMetalDeviceRead(ctx context.Context, d *schema.ResourceData, meta
return getNetworkRank(int(famI), pubI) < getNetworkRank(int(famJ), pubJ)
})

d.Set("network", networkInfo.Networks)
d.Set("access_public_ipv4", networkInfo.PublicIPv4)
d.Set("access_private_ipv4", networkInfo.PrivateIPv4)
d.Set("access_public_ipv6", networkInfo.PublicIPv6)
errs = append(errs, d.Set("network", networkInfo.Networks))
errs = append(errs, d.Set("access_public_ipv4", networkInfo.PublicIPv4))
errs = append(errs, d.Set("access_private_ipv4", networkInfo.PrivateIPv4))
errs = append(errs, d.Set("access_public_ipv6", networkInfo.PublicIPv6))

ports := getPorts(device.NetworkPorts)
d.Set("ports", ports)
errs = append(errs, d.Set("ports", ports))

d.SetId(device.GetId())

err = errors.Join(errs...)
if err != nil {
return diag.FromErr(err)
}

return nil
}

Expand Down
Loading
Loading