-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: metal_devices datasource (#366)
This PR adds `metal_devices` datasource, which fetches lists of device, similarly to [metal_plans](https://github.com/equinix/terraform-provider-equinix/blob/main/equinix/data_source_metal_plans.go) ([doc](https://registry.terraform.io/providers/equinix/equinix/latest/docs/data-sources/equinix_metal_plans)) fixes #288 The datasource is using metal-go.
- Loading branch information
Showing
6 changed files
with
339 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
--- | ||
subcategory: "Metal" | ||
--- | ||
|
||
# equinix_metal_devices | ||
|
||
The datasource can be used to find a list of devices which meet filter criteria. | ||
|
||
If you need to fetch a single device by ID or by project ID and hostname, use the [equinix_metal_device](equinix_metal_device.md) datasource. | ||
|
||
## Example Usage | ||
|
||
```hcl | ||
# Following example will select c3.small.x86 devices which are deplyed in metro 'da' (Dallas) | ||
# OR 'sv' (Sillicon Valley). | ||
data "equinix_metal_devices" "example" { | ||
project_id = local.project_id | ||
filter { | ||
attribute = "plan" | ||
values = ["c3.small.x86"] | ||
} | ||
filter { | ||
attribute = "metro" | ||
values = ["da", "sv"] | ||
} | ||
} | ||
output "devices" { | ||
organization_id = local.org_id | ||
value = data.equinix_metal_devices.example.devices | ||
} | ||
``` | ||
|
||
```hcl | ||
# Following example takes advantage of the `search` field in the API request, and will select devices with | ||
# string "database" in one of the searched attributes. See `search` in argument reference. | ||
data "equinix_metal_devices" "example" { | ||
search = "database" | ||
} | ||
output "devices" { | ||
value = data.equinix_metal_devices.example.devices | ||
} | ||
``` | ||
|
||
## search vs filter | ||
|
||
The difference between `search` and `filter` is that `search` is an API parameter, interpreted by the Equinix Metal service. The "filter" arguments will reduce the API list (or search) results by applying client-side filtering, within this provider. | ||
|
||
## Argument Reference | ||
|
||
The following arguments are supported: | ||
|
||
* `project_id` - (Optional) ID of project containing the devices. Exactly one of `project_id` and `organization_id` must be set. | ||
* `organization_id` - (Optional) ID of organization containing the devices. | ||
* `search` - (Optional) - Search string to filter devices by hostname, description, short_id, reservation short_id, tags, plan name, plan slug, facility code, facility name, operating system name, operating system slug, IP addresses. | ||
* `filter` - (Optional) One or more attribute/values pairs to filter. List of atributes to filter can be found in the [attribute reference](equinix_metal_device.md#attributes-reference) of the `equinix_metal_device` datasource. | ||
- `attribute` - (Required) The attribute used to filter. Filter attributes are case-sensitive | ||
- `values` - (Required) The filter values. Filter values are case-sensitive. If you specify multiple values for a filter, the values are joined with an OR by default, and the request returns all results that match any of the specified values | ||
- `match_by` - (Optional) The type of comparison to apply. One of: `in` , `re`, `substring`, `less_than`, `less_than_or_equal`, `greater_than`, `greater_than_or_equal`. Default is `in`. | ||
- `all` - (Optional) If is set to true, the values are joined with an AND, and the requests returns only the results that match all specified values. Default is `false`. | ||
|
||
All fields in the `devices` block defined below can be used as attribute for both `sort` and `filter` blocks. | ||
|
||
## Attributes Reference | ||
|
||
In addition to all arguments above, the following attributes are exported: | ||
|
||
* `devices` - list of resources with attributes like in the [equninix_metal_device datasources](equinix_metal_device.md). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package equinix | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
metalv1 "github.com/equinix-labs/metal-go/metal/v1" | ||
"github.com/equinix/terraform-provider-equinix/equinix/internal/datalist" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
) | ||
|
||
func dataSourceMetalDevices() *schema.Resource { | ||
dsmd := dataSourceMetalDevice() | ||
sch := dsmd.Schema | ||
for _, v := range sch { | ||
if v.Optional { | ||
v.Optional = false | ||
} | ||
if v.ConflictsWith != nil { | ||
v.ConflictsWith = nil | ||
} | ||
} | ||
dataListConfig := &datalist.ResourceConfig{ | ||
RecordSchema: sch, | ||
ResultAttributeName: "devices", | ||
ResultAttributeDescription: "List of devices that match specified filters", | ||
FlattenRecord: flattenDevice, | ||
GetRecords: getDevices, | ||
ExtraQuerySchema: map[string]*schema.Schema{ | ||
"project_id": { | ||
Type: schema.TypeString, | ||
Description: "The id of the project to query for devices", | ||
Optional: true, | ||
ConflictsWith: []string{"organization_id"}, | ||
}, | ||
"organization_id": { | ||
Type: schema.TypeString, | ||
Description: "The id of the organization to query for devices", | ||
Optional: true, | ||
ConflictsWith: []string{"project_id"}, | ||
}, | ||
"search": { | ||
Type: schema.TypeString, | ||
Description: "Search string to filter devices by hostname, description, short_id, reservation short_id, tags, plan name, plan slug, facility code, facility name, operating system name, operating system slug, IP addresses.", | ||
Optional: true, | ||
}, | ||
}, | ||
} | ||
return datalist.NewResource(dataListConfig) | ||
} | ||
|
||
func getDevices(meta interface{}, extra map[string]interface{}) ([]interface{}, error) { | ||
client := meta.(*Config).metalgo | ||
projectID := extra["project_id"].(string) | ||
orgID := extra["organization_id"].(string) | ||
|
||
if (len(projectID) == 0) && (len(orgID) == 0) { | ||
return nil, fmt.Errorf("one of project_id or organization_id must be specified") | ||
} | ||
|
||
search := extra["search"].(string) | ||
|
||
var devices *metalv1.DeviceList | ||
devicesIf := []interface{}{} | ||
var err error | ||
|
||
if len(projectID) > 0 { | ||
query := client.DevicesApi.FindProjectDevices( | ||
context.Background(), projectID).Include(deviceCommonIncludes) | ||
if len(search) > 0 { | ||
query = query.Search(search) | ||
} | ||
devices, _, err = query.Execute() | ||
} | ||
|
||
if len(orgID) > 0 { | ||
query := client.DevicesApi.FindOrganizationDevices( | ||
context.Background(), orgID).Include(deviceCommonIncludes) | ||
if len(search) > 0 { | ||
query = query.Search(search) | ||
} | ||
devices, _, err = query.Execute() | ||
} | ||
|
||
for _, d := range devices.Devices { | ||
devicesIf = append(devicesIf, d) | ||
} | ||
return devicesIf, err | ||
} | ||
|
||
func flattenDevice(rawDevice interface{}, meta interface{}, extra map[string]interface{}) (map[string]interface{}, error) { | ||
device, ok := rawDevice.(metalv1.Device) | ||
if !ok { | ||
return nil, fmt.Errorf("expected device to be of type *metalv1.Device, got %T", rawDevice) | ||
} | ||
return getDeviceMap(device), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package equinix | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" | ||
) | ||
|
||
func TestAccDataSourceMetalDevices(t *testing.T) { | ||
projectName := fmt.Sprintf("ds-device-%s", acctest.RandString(10)) | ||
|
||
resource.ParallelTest(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
ExternalProviders: testExternalProviders, | ||
Providers: testAccProviders, | ||
CheckDestroy: testAccMetalDeviceCheckDestroyed, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testDataSourceMetalDevicesConfig_basic(projectName), | ||
Check: resource.ComposeTestCheckFunc( | ||
resource.TestCheckResourceAttr( | ||
"data.equinix_metal_devices.test_filter_tags", "devices.#", "1"), | ||
resource.TestCheckResourceAttr( | ||
"data.equinix_metal_devices.test_search", "devices.#", "1"), | ||
resource.TestCheckResourceAttrPair( | ||
"equinix_metal_device.dev_tags", "id", | ||
"data.equinix_metal_devices.test_filter_tags", "devices.0.device_id"), | ||
resource.TestCheckResourceAttrPair( | ||
"equinix_metal_device.dev_search", "id", | ||
"data.equinix_metal_devices.test_search", "devices.0.device_id"), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testDataSourceMetalDevicesConfig_basic(projSuffix string) string { | ||
return fmt.Sprintf(` | ||
%[1]s | ||
resource "equinix_metal_project" "test" { | ||
name = "tfacc-project-%[2]s" | ||
} | ||
resource "equinix_metal_device" "dev_tags" { | ||
hostname = "tfacc-test-device1" | ||
plan = local.plan | ||
metro = local.metro | ||
operating_system = local.os | ||
billing_cycle = "hourly" | ||
project_id = "${equinix_metal_project.test.id}" | ||
termination_time = "%[3]s" | ||
tags = ["tag1", "tag2"] | ||
} | ||
resource "equinix_metal_device" "dev_search" { | ||
hostname = "tfacc-test-device2-unlikelystring" | ||
plan = local.plan | ||
metro = local.metro | ||
operating_system = local.os | ||
billing_cycle = "hourly" | ||
project_id = "${equinix_metal_project.test.id}" | ||
termination_time = "%[3]s" | ||
} | ||
data "equinix_metal_devices" "test_filter_tags" { | ||
project_id = equinix_metal_project.test.id | ||
filter { | ||
attribute = "tags" | ||
values = ["tag1"] | ||
} | ||
depends_on = [equinix_metal_device.dev_tags] | ||
} | ||
data "equinix_metal_devices" "test_search" { | ||
project_id = equinix_metal_project.test.id | ||
search = "unlikelystring" | ||
depends_on = [equinix_metal_device.dev_search] | ||
}`, confAccMetalDevice_base(preferable_plans, preferable_metros, preferable_os), projSuffix, testDeviceTerminationTime()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters