Feat/add datasource to query machine types (#968)
* feat(iaas): add datasource to query machine types Signed-off-by: Mauritz Uphoff <mauritz.uphoff@stackit.cloud> * review Signed-off-by: Mauritz Uphoff <mauritz.uphoff@stackit.cloud> * review Signed-off-by: Mauritz Uphoff <mauritz.uphoff@stackit.cloud> --------- Signed-off-by: Mauritz Uphoff <mauritz.uphoff@stackit.cloud>
This commit is contained in:
parent
64787fff67
commit
fedaf72b62
8 changed files with 662 additions and 0 deletions
70
docs/data-sources/machine_type.md
Normal file
70
docs/data-sources/machine_type.md
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "stackit_machine_type Data Source - stackit"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
Machine type data source.
|
||||
~> This datasource is in beta and may be subject to breaking changes in the future. Use with caution. See our guide https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/opting_into_beta_resources for how to opt-in to use beta resources.
|
||||
---
|
||||
|
||||
# stackit_machine_type (Data Source)
|
||||
|
||||
Machine type data source.
|
||||
|
||||
~> This datasource is in beta and may be subject to breaking changes in the future. Use with caution. See our [guide](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/opting_into_beta_resources) for how to opt-in to use beta resources.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
data "stackit_machine_type" "two_vcpus_filter" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
filter = "vcpus==2"
|
||||
}
|
||||
|
||||
data "stackit_machine_type" "filter_sorted_ascending_false" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
filter = "vcpus >= 2 && ram >= 2048"
|
||||
sort_ascending = false
|
||||
}
|
||||
|
||||
data "stackit_machine_type" "intel_icelake_generic_filter" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
filter = "extraSpecs.cpu==\"intel-icelake-generic\" && vcpus == 2"
|
||||
}
|
||||
|
||||
# returns warning
|
||||
data "stackit_machine_type" "no_match" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
filter = "vcpus == 99"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `filter` (String) Expr-lang filter for filtering machine types.
|
||||
|
||||
Examples:
|
||||
- vcpus == 2
|
||||
- ram >= 2048
|
||||
- extraSpecs.cpu == "intel-icelake-generic"
|
||||
- extraSpecs.cpu == "intel-icelake-generic" && vcpus == 2
|
||||
|
||||
See https://expr-lang.org/docs/language-definition for syntax.
|
||||
- `project_id` (String) STACKIT Project ID.
|
||||
|
||||
### Optional
|
||||
|
||||
- `sort_ascending` (Boolean) Sort machine types by name ascending (`true`) or descending (`false`). Defaults to `false`
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `description` (String) Machine type description.
|
||||
- `disk` (Number) Disk size in GB.
|
||||
- `extra_specs` (Map of String) Extra specs (e.g., CPU type, overcommit ratio).
|
||||
- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`image_id`".
|
||||
- `name` (String) Name of the machine type (e.g. 's1.2').
|
||||
- `ram` (Number) RAM size in MB.
|
||||
- `vcpus` (Number) Number of vCPUs.
|
||||
21
examples/data-sources/stackit_machine_type/data-source.tf
Normal file
21
examples/data-sources/stackit_machine_type/data-source.tf
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
data "stackit_machine_type" "two_vcpus_filter" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
filter = "vcpus==2"
|
||||
}
|
||||
|
||||
data "stackit_machine_type" "filter_sorted_ascending_false" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
filter = "vcpus >= 2 && ram >= 2048"
|
||||
sort_ascending = false
|
||||
}
|
||||
|
||||
data "stackit_machine_type" "intel_icelake_generic_filter" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
filter = "extraSpecs.cpu==\"intel-icelake-generic\" && vcpus == 2"
|
||||
}
|
||||
|
||||
# returns warning
|
||||
data "stackit_machine_type" "no_match" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
filter = "vcpus == 99"
|
||||
}
|
||||
|
|
@ -88,6 +88,9 @@ var (
|
|||
|
||||
//go:embed testdata/resource-server-max-server-attachments.tf
|
||||
resourceServerMaxAttachmentConfig string
|
||||
|
||||
//go:embed testdata/datasource-machinetype.tf
|
||||
dataSourceMachineTypeConfig string
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -487,6 +490,10 @@ var testConfigKeyPairMaxUpdated = func() config.Variables {
|
|||
return updatedConfig
|
||||
}()
|
||||
|
||||
var testConfigMachineTypeVars = config.Variables{
|
||||
"project_id": config.StringVariable(testutil.ProjectId),
|
||||
}
|
||||
|
||||
// if no local file is provided the test should create a default file and work with this instead of failing
|
||||
var localFileForIaasImage os.File
|
||||
|
||||
|
|
@ -4054,6 +4061,47 @@ func TestAccProject(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAccMachineType(t *testing.T) {
|
||||
t.Logf("TestAccMachineType projectid: %s", testutil.ConvertConfigVariable(testConfigMachineTypeVars["project_id"]))
|
||||
resource.ParallelTest(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
ConfigVariables: testConfigMachineTypeVars,
|
||||
Config: fmt.Sprintf("%s\n%s", dataSourceMachineTypeConfig, testutil.IaaSProviderConfigWithBetaResourcesEnabled()),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("data.stackit_machine_type.two_vcpus_filter", "project_id", testutil.ConvertConfigVariable(testConfigMachineTypeVars["project_id"])),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_machine_type.two_vcpus_filter", "id"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_machine_type.two_vcpus_filter", "name"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_machine_type.two_vcpus_filter", "vcpus"),
|
||||
resource.TestCheckResourceAttr("data.stackit_machine_type.two_vcpus_filter", "vcpus", "2"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_machine_type.two_vcpus_filter", "ram"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_machine_type.two_vcpus_filter", "disk"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_machine_type.two_vcpus_filter", "description"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_machine_type.two_vcpus_filter", "extra_specs.cpu"),
|
||||
|
||||
resource.TestCheckResourceAttr("data.stackit_machine_type.filter_sorted_ascending_false", "project_id", testutil.ConvertConfigVariable(testConfigMachineTypeVars["project_id"])),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_machine_type.filter_sorted_ascending_false", "id"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_machine_type.filter_sorted_ascending_false", "name"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_machine_type.filter_sorted_ascending_false", "vcpus"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_machine_type.filter_sorted_ascending_false", "ram"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_machine_type.filter_sorted_ascending_false", "disk"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_machine_type.filter_sorted_ascending_false", "description"),
|
||||
resource.TestCheckResourceAttrSet("data.stackit_machine_type.filter_sorted_ascending_false", "extra_specs.cpu"),
|
||||
|
||||
resource.TestCheckResourceAttr("data.stackit_machine_type.no_match", "project_id", testutil.ConvertConfigVariable(testConfigMachineTypeVars["project_id"])),
|
||||
resource.TestCheckNoResourceAttr("data.stackit_machine_type.no_match", "description"),
|
||||
resource.TestCheckNoResourceAttr("data.stackit_machine_type.no_match", "disk"),
|
||||
resource.TestCheckNoResourceAttr("data.stackit_machine_type.no_match", "extra_specs"),
|
||||
resource.TestCheckNoResourceAttr("data.stackit_machine_type.no_match", "id"),
|
||||
resource.TestCheckNoResourceAttr("data.stackit_machine_type.no_match", "name"),
|
||||
resource.TestCheckNoResourceAttr("data.stackit_machine_type.no_match", "ram"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckDestroy(s *terraform.State) error {
|
||||
checkFunctions := []func(s *terraform.State) error{
|
||||
testAccCheckNetworkV1Destroy,
|
||||
|
|
|
|||
245
stackit/internal/services/iaas/machinetype/datasource.go
Normal file
245
stackit/internal/services/iaas/machinetype/datasource.go
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
package machineType
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/features"
|
||||
iaasUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/utils"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
|
||||
)
|
||||
|
||||
// Ensure the implementation satisfies the expected interfaces.
|
||||
var _ datasource.DataSource = &machineTypeDataSource{}
|
||||
|
||||
type DataSourceModel struct {
|
||||
Id types.String `tfsdk:"id"` // required by Terraform to identify state
|
||||
ProjectId types.String `tfsdk:"project_id"`
|
||||
SortAscending types.Bool `tfsdk:"sort_ascending"`
|
||||
Filter types.String `tfsdk:"filter"`
|
||||
Description types.String `tfsdk:"description"`
|
||||
Disk types.Int64 `tfsdk:"disk"`
|
||||
ExtraSpecs types.Map `tfsdk:"extra_specs"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Ram types.Int64 `tfsdk:"ram"`
|
||||
Vcpus types.Int64 `tfsdk:"vcpus"`
|
||||
}
|
||||
|
||||
// NewMachineTypeDataSource instantiates the data source
|
||||
func NewMachineTypeDataSource() datasource.DataSource {
|
||||
return &machineTypeDataSource{}
|
||||
}
|
||||
|
||||
type machineTypeDataSource struct {
|
||||
client *iaas.APIClient
|
||||
}
|
||||
|
||||
func (d *machineTypeDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_machine_type"
|
||||
}
|
||||
|
||||
func (d *machineTypeDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
||||
providerData, ok := conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
features.CheckBetaResourcesEnabled(ctx, &providerData, &resp.Diagnostics, "stackit_machine_type", "datasource")
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
client := iaasUtils.ConfigureClient(ctx, &providerData, &resp.Diagnostics)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
d.client = client
|
||||
|
||||
tflog.Info(ctx, "IAAS client configured")
|
||||
}
|
||||
|
||||
func (d *machineTypeDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
resp.Schema = schema.Schema{
|
||||
MarkdownDescription: features.AddBetaDescription("Machine type data source.", core.Datasource),
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.StringAttribute{
|
||||
Description: "Terraform's internal resource ID. It is structured as \"`project_id`,`image_id`\".",
|
||||
Computed: true,
|
||||
},
|
||||
"project_id": schema.StringAttribute{
|
||||
Description: "STACKIT Project ID.",
|
||||
Required: true,
|
||||
Validators: []validator.String{
|
||||
validate.UUID(),
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
"sort_ascending": schema.BoolAttribute{
|
||||
Description: "Sort machine types by name ascending (`true`) or descending (`false`). Defaults to `false`",
|
||||
Optional: true,
|
||||
},
|
||||
"filter": schema.StringAttribute{
|
||||
Description: `Expr-lang filter for filtering machine types.
|
||||
|
||||
Examples:
|
||||
- vcpus == 2
|
||||
- ram >= 2048
|
||||
- extraSpecs.cpu == "intel-icelake-generic"
|
||||
- extraSpecs.cpu == "intel-icelake-generic" && vcpus == 2
|
||||
|
||||
See https://expr-lang.org/docs/language-definition for syntax.`,
|
||||
Required: true,
|
||||
},
|
||||
"description": schema.StringAttribute{
|
||||
Description: "Machine type description.",
|
||||
Computed: true,
|
||||
},
|
||||
"disk": schema.Int64Attribute{
|
||||
Description: "Disk size in GB.",
|
||||
Computed: true,
|
||||
},
|
||||
"extra_specs": schema.MapAttribute{
|
||||
Description: "Extra specs (e.g., CPU type, overcommit ratio).",
|
||||
ElementType: types.StringType,
|
||||
Computed: true,
|
||||
},
|
||||
"name": schema.StringAttribute{
|
||||
Description: "Name of the machine type (e.g. 's1.2').",
|
||||
Computed: true,
|
||||
},
|
||||
"ram": schema.Int64Attribute{
|
||||
Description: "RAM size in MB.",
|
||||
Computed: true,
|
||||
},
|
||||
"vcpus": schema.Int64Attribute{
|
||||
Description: "Number of vCPUs.",
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (d *machineTypeDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
var model DataSourceModel
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &model)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
projectId := model.ProjectId.ValueString()
|
||||
sortAscending := model.SortAscending.ValueBool()
|
||||
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "filter_is_null", model.Filter.IsNull())
|
||||
ctx = tflog.SetField(ctx, "filter_is_unknown", model.Filter.IsUnknown())
|
||||
|
||||
listMachineTypeReq := d.client.ListMachineTypes(ctx, projectId)
|
||||
|
||||
if !model.Filter.IsNull() && !model.Filter.IsUnknown() && strings.TrimSpace(model.Filter.ValueString()) != "" {
|
||||
listMachineTypeReq = listMachineTypeReq.Filter(strings.TrimSpace(model.Filter.ValueString()))
|
||||
}
|
||||
|
||||
apiResp, err := listMachineTypeReq.Execute()
|
||||
if err != nil {
|
||||
utils.LogError(ctx, &resp.Diagnostics, err, "Failed to read machine types",
|
||||
fmt.Sprintf("Unable to retrieve machine types for project %q %s.", projectId, err),
|
||||
map[int]string{
|
||||
http.StatusForbidden: fmt.Sprintf("Access denied to project %q.", projectId),
|
||||
},
|
||||
)
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
if apiResp.Items == nil || len(*apiResp.Items) == 0 {
|
||||
core.LogAndAddWarning(ctx, &resp.Diagnostics, "No machine types found", "No matching machine types.")
|
||||
return
|
||||
}
|
||||
|
||||
// Convert items to []*iaas.MachineType
|
||||
machineTypes := make([]*iaas.MachineType, len(*apiResp.Items))
|
||||
for i := range *apiResp.Items {
|
||||
machineTypes[i] = &(*apiResp.Items)[i]
|
||||
}
|
||||
|
||||
sorted, err := sortMachineTypeByName(machineTypes, sortAscending)
|
||||
if err != nil {
|
||||
core.LogAndAddWarning(ctx, &resp.Diagnostics, "Unable to sort", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := mapDataSourceFields(ctx, sorted[0], &model); err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading machine type", fmt.Sprintf("Failed to translate API response: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, model)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "Successfully read machine type")
|
||||
}
|
||||
|
||||
func mapDataSourceFields(ctx context.Context, machineType *iaas.MachineType, model *DataSourceModel) error {
|
||||
if machineType == nil || model == nil {
|
||||
return fmt.Errorf("nil input provided")
|
||||
}
|
||||
|
||||
if machineType.Name == nil || *machineType.Name == "" {
|
||||
return fmt.Errorf("machine type name is missing")
|
||||
}
|
||||
|
||||
model.Id = utils.BuildInternalTerraformId(model.ProjectId.ValueString(), *machineType.Name)
|
||||
model.Name = types.StringPointerValue(machineType.Name)
|
||||
model.Description = types.StringPointerValue(machineType.Description)
|
||||
model.Disk = types.Int64PointerValue(machineType.Disk)
|
||||
model.Ram = types.Int64PointerValue(machineType.Ram)
|
||||
model.Vcpus = types.Int64PointerValue(machineType.Vcpus)
|
||||
|
||||
extra := types.MapNull(types.StringType)
|
||||
if machineType.ExtraSpecs != nil && len(*machineType.ExtraSpecs) > 0 {
|
||||
var diags diag.Diagnostics
|
||||
extra, diags = types.MapValueFrom(ctx, types.StringType, *machineType.ExtraSpecs)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("converting extraspecs: %w", core.DiagsToError(diags))
|
||||
}
|
||||
}
|
||||
model.ExtraSpecs = extra
|
||||
return nil
|
||||
}
|
||||
|
||||
func sortMachineTypeByName(input []*iaas.MachineType, ascending bool) ([]*iaas.MachineType, error) {
|
||||
if input == nil {
|
||||
return nil, fmt.Errorf("input slice is nil")
|
||||
}
|
||||
|
||||
// Filter out nil or missing name
|
||||
var filtered []*iaas.MachineType
|
||||
for _, m := range input {
|
||||
if m != nil && m.Name != nil {
|
||||
filtered = append(filtered, m)
|
||||
}
|
||||
}
|
||||
|
||||
sort.SliceStable(filtered, func(i, j int) bool {
|
||||
if ascending {
|
||||
return *filtered[i].Name < *filtered[j].Name
|
||||
}
|
||||
return *filtered[i].Name > *filtered[j].Name
|
||||
})
|
||||
|
||||
return filtered, nil
|
||||
}
|
||||
241
stackit/internal/services/iaas/machinetype/datasource_test.go
Normal file
241
stackit/internal/services/iaas/machinetype/datasource_test.go
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
package machineType
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
|
||||
)
|
||||
|
||||
func TestMapDataSourceFields(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
initial DataSourceModel
|
||||
input *iaas.MachineType
|
||||
expected DataSourceModel
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "valid simple values",
|
||||
initial: DataSourceModel{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
},
|
||||
input: &iaas.MachineType{
|
||||
Name: utils.Ptr("s1.2"),
|
||||
Description: utils.Ptr("general-purpose small"),
|
||||
Disk: utils.Ptr(int64(20)),
|
||||
Ram: utils.Ptr(int64(2048)),
|
||||
Vcpus: utils.Ptr(int64(2)),
|
||||
ExtraSpecs: &map[string]interface{}{
|
||||
"cpu": "amd-epycrome-7702",
|
||||
"overcommit": "1",
|
||||
"environment": "general",
|
||||
},
|
||||
},
|
||||
expected: DataSourceModel{
|
||||
Id: types.StringValue("pid,s1.2"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
Name: types.StringValue("s1.2"),
|
||||
Description: types.StringValue("general-purpose small"),
|
||||
Disk: types.Int64Value(20),
|
||||
Ram: types.Int64Value(2048),
|
||||
Vcpus: types.Int64Value(2),
|
||||
ExtraSpecs: types.MapValueMust(types.StringType, map[string]attr.Value{
|
||||
"cpu": types.StringValue("amd-epycrome-7702"),
|
||||
"overcommit": types.StringValue("1"),
|
||||
"environment": types.StringValue("general"),
|
||||
}),
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "missing name should fail",
|
||||
initial: DataSourceModel{
|
||||
ProjectId: types.StringValue("pid-456"),
|
||||
},
|
||||
input: &iaas.MachineType{
|
||||
Description: utils.Ptr("gp-medium"),
|
||||
},
|
||||
expected: DataSourceModel{},
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "nil machineType should fail",
|
||||
initial: DataSourceModel{},
|
||||
input: nil,
|
||||
expected: DataSourceModel{},
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "empty extraSpecs should return null map",
|
||||
initial: DataSourceModel{
|
||||
ProjectId: types.StringValue("pid-789"),
|
||||
},
|
||||
input: &iaas.MachineType{
|
||||
Name: utils.Ptr("m1.noextras"),
|
||||
Description: utils.Ptr("no extras"),
|
||||
Disk: utils.Ptr(int64(10)),
|
||||
Ram: utils.Ptr(int64(1024)),
|
||||
Vcpus: utils.Ptr(int64(1)),
|
||||
ExtraSpecs: &map[string]interface{}{},
|
||||
},
|
||||
expected: DataSourceModel{
|
||||
Id: types.StringValue("pid-789,m1.noextras"),
|
||||
ProjectId: types.StringValue("pid-789"),
|
||||
Name: types.StringValue("m1.noextras"),
|
||||
Description: types.StringValue("no extras"),
|
||||
Disk: types.Int64Value(10),
|
||||
Ram: types.Int64Value(1024),
|
||||
Vcpus: types.Int64Value(1),
|
||||
ExtraSpecs: types.MapNull(types.StringType),
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "nil extrasSpecs should return null map",
|
||||
initial: DataSourceModel{
|
||||
ProjectId: types.StringValue("pid-987"),
|
||||
},
|
||||
input: &iaas.MachineType{
|
||||
Name: utils.Ptr("g1.nil"),
|
||||
Description: utils.Ptr("missing extras"),
|
||||
Disk: utils.Ptr(int64(40)),
|
||||
Ram: utils.Ptr(int64(8096)),
|
||||
Vcpus: utils.Ptr(int64(4)),
|
||||
ExtraSpecs: nil,
|
||||
},
|
||||
expected: DataSourceModel{
|
||||
Id: types.StringValue("pid-987,g1.nil"),
|
||||
ProjectId: types.StringValue("pid-987"),
|
||||
Name: types.StringValue("g1.nil"),
|
||||
Description: types.StringValue("missing extras"),
|
||||
Disk: types.Int64Value(40),
|
||||
Ram: types.Int64Value(8096),
|
||||
Vcpus: types.Int64Value(4),
|
||||
ExtraSpecs: types.MapNull(types.StringType),
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "invalid extraSpecs with non-string values",
|
||||
initial: DataSourceModel{
|
||||
ProjectId: types.StringValue("test-err"),
|
||||
},
|
||||
input: &iaas.MachineType{
|
||||
Name: utils.Ptr("invalid"),
|
||||
Description: utils.Ptr("bad map"),
|
||||
Disk: utils.Ptr(int64(10)),
|
||||
Ram: utils.Ptr(int64(4096)),
|
||||
Vcpus: utils.Ptr(int64(2)),
|
||||
ExtraSpecs: &map[string]interface{}{
|
||||
"cpu": "intel",
|
||||
"burst": true, // not a string
|
||||
"gen": 8, // not a string
|
||||
},
|
||||
},
|
||||
expected: DataSourceModel{},
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := mapDataSourceFields(context.Background(), tt.input, &tt.initial)
|
||||
if tt.expectError {
|
||||
if err == nil {
|
||||
t.Errorf("expected error but got none")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
diff := cmp.Diff(tt.expected, tt.initial)
|
||||
if diff != "" {
|
||||
t.Errorf("unexpected diff (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
// Extra sanity check for proper ID format
|
||||
if id := tt.initial.Id.ValueString(); !strings.HasPrefix(id, tt.initial.ProjectId.ValueString()+",") {
|
||||
t.Errorf("unexpected ID format: got %q", id)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSortMachineTypeByName(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input []*iaas.MachineType
|
||||
ascending bool
|
||||
expected []string
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "ascending order",
|
||||
input: []*iaas.MachineType{{Name: utils.Ptr("zeta")}, {Name: utils.Ptr("alpha")}, {Name: utils.Ptr("gamma")}},
|
||||
ascending: true,
|
||||
expected: []string{"alpha", "gamma", "zeta"},
|
||||
},
|
||||
{
|
||||
name: "descending order",
|
||||
input: []*iaas.MachineType{{Name: utils.Ptr("zeta")}, {Name: utils.Ptr("alpha")}, {Name: utils.Ptr("gamma")}},
|
||||
ascending: false,
|
||||
expected: []string{"zeta", "gamma", "alpha"},
|
||||
},
|
||||
{
|
||||
name: "handles nil names",
|
||||
input: []*iaas.MachineType{{Name: utils.Ptr("beta")}, nil, {Name: nil}, {Name: utils.Ptr("alpha")}},
|
||||
ascending: true,
|
||||
expected: []string{"alpha", "beta"},
|
||||
},
|
||||
{
|
||||
name: "empty input",
|
||||
input: []*iaas.MachineType{},
|
||||
ascending: true,
|
||||
expected: nil,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "nil input",
|
||||
input: nil,
|
||||
ascending: true,
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sorted, err := sortMachineTypeByName(tt.input, tt.ascending)
|
||||
|
||||
if tt.expectError {
|
||||
if err == nil {
|
||||
t.Errorf("expected error but got none")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
var result []string
|
||||
for _, mt := range sorted {
|
||||
if mt.Name != nil {
|
||||
result = append(result, *mt.Name)
|
||||
}
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.expected, result); diff != "" {
|
||||
t.Errorf("unexpected sorted order (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
18
stackit/internal/services/iaas/testdata/datasource-machinetype.tf
vendored
Normal file
18
stackit/internal/services/iaas/testdata/datasource-machinetype.tf
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
variable "project_id" {}
|
||||
|
||||
data "stackit_machine_type" "two_vcpus_filter" {
|
||||
project_id = var.project_id
|
||||
filter = "vcpus==2"
|
||||
}
|
||||
|
||||
data "stackit_machine_type" "filter_sorted_ascending_false" {
|
||||
project_id = var.project_id
|
||||
filter = "vcpus >= 2 && ram >= 2048"
|
||||
sort_ascending = false
|
||||
}
|
||||
|
||||
# returns warning
|
||||
data "stackit_machine_type" "no_match" {
|
||||
project_id = var.project_id
|
||||
filter = "vcpus == 99"
|
||||
}
|
||||
|
|
@ -136,6 +136,23 @@ func IaaSProviderConfig() string {
|
|||
)
|
||||
}
|
||||
|
||||
func IaaSProviderConfigWithBetaResourcesEnabled() string {
|
||||
if IaaSCustomEndpoint == "" {
|
||||
return `
|
||||
provider "stackit" {
|
||||
enable_beta_resources = true
|
||||
default_region = "eu01"
|
||||
}`
|
||||
}
|
||||
return fmt.Sprintf(`
|
||||
provider "stackit" {
|
||||
enable_beta_resources = true
|
||||
iaas_custom_endpoint = "%s"
|
||||
}`,
|
||||
IaaSCustomEndpoint,
|
||||
)
|
||||
}
|
||||
|
||||
func IaaSProviderConfigWithExperiments() string {
|
||||
if IaaSCustomEndpoint == "" {
|
||||
return `
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import (
|
|||
iaasAffinityGroup "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/affinitygroup"
|
||||
iaasImage "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/image"
|
||||
iaasKeyPair "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/keypair"
|
||||
machineType "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/machinetype"
|
||||
iaasNetwork "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/network"
|
||||
iaasNetworkArea "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/networkarea"
|
||||
iaasNetworkAreaRoute "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/networkarearoute"
|
||||
|
|
@ -476,6 +477,7 @@ func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource
|
|||
logMeInstance.NewInstanceDataSource,
|
||||
logMeCredential.NewCredentialDataSource,
|
||||
logAlertGroup.NewLogAlertGroupDataSource,
|
||||
machineType.NewMachineTypeDataSource,
|
||||
mariaDBInstance.NewInstanceDataSource,
|
||||
mariaDBCredential.NewCredentialDataSource,
|
||||
mongoDBFlexInstance.NewInstanceDataSource,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue