0
0
mirror of https://github.com/bpg/terraform-provider-proxmox.git synced 2025-08-24 04:18:33 +00:00

chore: cleanup resource validators & utility code (#438)

* chore: cleanup resource validators & utility code

* fix linter errors
This commit is contained in:
Pavel Boldyrev 2023-07-20 19:58:19 -04:00 committed by GitHub
parent 6781c03ca1
commit b2a27f3ccf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 723 additions and 873 deletions

View File

@ -72,7 +72,7 @@ init:
go get ./... go get ./...
test: test:
go test -v ./... go test ./...
lint: lint:
go run -modfile=tools/go.mod github.com/golangci/golangci-lint/cmd/golangci-lint run --fix go run -modfile=tools/go.mod github.com/golangci/golangci-lint/cmd/golangci-lint run --fix

View File

@ -12,7 +12,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test"
) )
// TestAliasSchemaInstantiation tests whether the AliasSchema instance can be instantiated. // TestAliasSchemaInstantiation tests whether the AliasSchema instance can be instantiated.
@ -25,18 +25,18 @@ func TestAliasSchemaInstantiation(t *testing.T) {
func TestAliasSchema(t *testing.T) { func TestAliasSchema(t *testing.T) {
t.Parallel() t.Parallel()
s := AliasSchema() r := schema.Resource{Schema: AliasSchema()}
structure.AssertRequiredArguments(t, s, []string{ test.AssertRequiredArguments(t, &r, []string{
mkAliasName, mkAliasName,
}) })
structure.AssertComputedAttributes(t, s, []string{ test.AssertComputedAttributes(t, &r, []string{
mkAliasCIDR, mkAliasCIDR,
mkAliasComment, mkAliasComment,
}) })
structure.AssertValueTypes(t, s, map[string]schema.ValueType{ test.AssertValueTypes(t, &r, map[string]schema.ValueType{
mkAliasName: schema.TypeString, mkAliasName: schema.TypeString,
mkAliasCIDR: schema.TypeString, mkAliasCIDR: schema.TypeString,
mkAliasComment: schema.TypeString, mkAliasComment: schema.TypeString,

View File

@ -12,7 +12,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test"
) )
// TestAliasesSchemaInstantiation tests whether the AliasesSchema instance can be instantiated. // TestAliasesSchemaInstantiation tests whether the AliasesSchema instance can be instantiated.
@ -25,13 +25,13 @@ func TestAliasesSchemaInstantiation(t *testing.T) {
func TestAliasesSchema(t *testing.T) { func TestAliasesSchema(t *testing.T) {
t.Parallel() t.Parallel()
s := AliasesSchema() r := schema.Resource{Schema: AliasesSchema()}
structure.AssertComputedAttributes(t, s, []string{ test.AssertComputedAttributes(t, &r, []string{
mkAliasesAliasNames, mkAliasesAliasNames,
}) })
structure.AssertValueTypes(t, s, map[string]schema.ValueType{ test.AssertValueTypes(t, &r, map[string]schema.ValueType{
mkAliasesAliasNames: schema.TypeList, mkAliasesAliasNames: schema.TypeList,
}) })
} }

View File

@ -12,7 +12,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test"
) )
// TestIPSetSchemaInstantiation tests whether the IPSetSchema instance can be instantiated. // TestIPSetSchemaInstantiation tests whether the IPSetSchema instance can be instantiated.
@ -25,32 +25,32 @@ func TestIPSetSchemaInstantiation(t *testing.T) {
func TestIPSetSchema(t *testing.T) { func TestIPSetSchema(t *testing.T) {
t.Parallel() t.Parallel()
s := IPSetSchema() r := schema.Resource{Schema: IPSetSchema()}
structure.AssertRequiredArguments(t, s, []string{ test.AssertRequiredArguments(t, &r, []string{
mkIPSetName, mkIPSetName,
}) })
structure.AssertComputedAttributes(t, s, []string{ test.AssertComputedAttributes(t, &r, []string{
mkIPSetCIDR, mkIPSetCIDR,
mkIPSetCIDRComment, mkIPSetCIDRComment,
}) })
structure.AssertValueTypes(t, s, map[string]schema.ValueType{ test.AssertValueTypes(t, &r, map[string]schema.ValueType{
mkIPSetName: schema.TypeString, mkIPSetName: schema.TypeString,
mkIPSetCIDR: schema.TypeList, mkIPSetCIDR: schema.TypeList,
mkIPSetCIDRComment: schema.TypeString, mkIPSetCIDRComment: schema.TypeString,
}) })
cirdSchema := structure.AssertNestedSchemaExistence(t, s, mkIPSetCIDR).Schema cird := test.AssertNestedSchemaExistence(t, &r, mkIPSetCIDR)
structure.AssertComputedAttributes(t, cirdSchema, []string{ test.AssertComputedAttributes(t, cird, []string{
mkIPSetCIDRName, mkIPSetCIDRName,
mkIPSetCIDRNoMatch, mkIPSetCIDRNoMatch,
mkIPSetCIDRComment, mkIPSetCIDRComment,
}) })
structure.AssertValueTypes(t, cirdSchema, map[string]schema.ValueType{ test.AssertValueTypes(t, cird, map[string]schema.ValueType{
mkIPSetCIDRName: schema.TypeString, mkIPSetCIDRName: schema.TypeString,
mkIPSetCIDRNoMatch: schema.TypeBool, mkIPSetCIDRNoMatch: schema.TypeBool,
mkIPSetCIDRComment: schema.TypeString, mkIPSetCIDRComment: schema.TypeString,

View File

@ -12,7 +12,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test"
) )
// TestIPSetsSchemaInstantiation tests whether the IPSetsSchema instance can be instantiated. // TestIPSetsSchemaInstantiation tests whether the IPSetsSchema instance can be instantiated.
@ -25,13 +25,13 @@ func TestIPSetsSchemaInstantiation(t *testing.T) {
func TestIPSetsSchema(t *testing.T) { func TestIPSetsSchema(t *testing.T) {
t.Parallel() t.Parallel()
s := IPSetsSchema() r := schema.Resource{Schema: IPSetsSchema()}
structure.AssertComputedAttributes(t, s, []string{ test.AssertComputedAttributes(t, &r, []string{
mkIPSetsIPSetNames, mkIPSetsIPSetNames,
}) })
structure.AssertValueTypes(t, s, map[string]schema.ValueType{ test.AssertValueTypes(t, &r, map[string]schema.ValueType{
mkIPSetsIPSetNames: schema.TypeList, mkIPSetsIPSetNames: schema.TypeList,
}) })
} }

View File

@ -12,7 +12,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test"
) )
// TestRuleInstantiation tests whether the RuleSchema instance can be instantiated. // TestRuleInstantiation tests whether the RuleSchema instance can be instantiated.
@ -25,14 +25,14 @@ func TestRuleSchemaInstantiation(t *testing.T) {
func TestRuleSchema(t *testing.T) { func TestRuleSchema(t *testing.T) {
t.Parallel() t.Parallel()
ruleSchema := RuleSchema() r := schema.Resource{Schema: RuleSchema()}
structure.AssertRequiredArguments(t, ruleSchema, []string{ test.AssertRequiredArguments(t, &r, []string{
mkRuleAction, mkRuleAction,
mkRuleType, mkRuleType,
}) })
structure.AssertComputedAttributes(t, ruleSchema, []string{ test.AssertComputedAttributes(t, &r, []string{
mkRuleComment, mkRuleComment,
mkRuleDest, mkRuleDest,
mkRuleDPort, mkRuleDPort,
@ -45,7 +45,7 @@ func TestRuleSchema(t *testing.T) {
mkRuleSPort, mkRuleSPort,
}) })
structure.AssertValueTypes(t, ruleSchema, map[string]schema.ValueType{ test.AssertValueTypes(t, &r, map[string]schema.ValueType{
mkRulePos: schema.TypeInt, mkRulePos: schema.TypeInt,
mkRuleAction: schema.TypeString, mkRuleAction: schema.TypeString,
mkRuleType: schema.TypeString, mkRuleType: schema.TypeString,

View File

@ -12,7 +12,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test"
) )
// TestSecurityGroupSchemaInstantiation tests whether the SecurityGroupSchema instance can be instantiated. // TestSecurityGroupSchemaInstantiation tests whether the SecurityGroupSchema instance can be instantiated.
@ -25,18 +25,18 @@ func TestSecurityGroupSchemaInstantiation(t *testing.T) {
func TestSecurityGroupSchema(t *testing.T) { func TestSecurityGroupSchema(t *testing.T) {
t.Parallel() t.Parallel()
s := SecurityGroupSchema() r := schema.Resource{Schema: SecurityGroupSchema()}
structure.AssertRequiredArguments(t, s, []string{ test.AssertRequiredArguments(t, &r, []string{
mkSecurityGroupName, mkSecurityGroupName,
}) })
structure.AssertComputedAttributes(t, s, []string{ test.AssertComputedAttributes(t, &r, []string{
mkSecurityGroupComment, mkSecurityGroupComment,
mkRules, mkRules,
}) })
structure.AssertValueTypes(t, s, map[string]schema.ValueType{ test.AssertValueTypes(t, &r, map[string]schema.ValueType{
mkSecurityGroupName: schema.TypeString, mkSecurityGroupName: schema.TypeString,
mkSecurityGroupComment: schema.TypeString, mkSecurityGroupComment: schema.TypeString,
mkRules: schema.TypeList, mkRules: schema.TypeList,

View File

@ -12,7 +12,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test"
) )
// TestSecurityGroupsSchemaInstantiation tests whether the SecurityGroupsSchema instance can be instantiated. // TestSecurityGroupsSchemaInstantiation tests whether the SecurityGroupsSchema instance can be instantiated.
@ -25,13 +25,13 @@ func TestSecurityGroupsSchemaInstantiation(t *testing.T) {
func TestSecurityGroupsSchema(t *testing.T) { func TestSecurityGroupsSchema(t *testing.T) {
t.Parallel() t.Parallel()
s := SecurityGroupsSchema() r := schema.Resource{Schema: SecurityGroupsSchema()}
structure.AssertComputedAttributes(t, s, []string{ test.AssertComputedAttributes(t, &r, []string{
mkSecurityGroupsSecurityGroupNames, mkSecurityGroupsSecurityGroupNames,
}) })
structure.AssertValueTypes(t, s, map[string]schema.ValueType{ test.AssertValueTypes(t, &r, map[string]schema.ValueType{
mkSecurityGroupsSecurityGroupNames: schema.TypeList, mkSecurityGroupsSecurityGroupNames: schema.TypeList,
}) })
} }

View File

@ -13,7 +13,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/firewall" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/firewall"
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test"
) )
// TestSecurityGroupInstantiation tests whether the SecurityGroup instance can be instantiated. // TestSecurityGroupInstantiation tests whether the SecurityGroup instance can be instantiated.
@ -26,20 +26,20 @@ func TestSecurityGroupInstantiation(t *testing.T) {
func TestSecurityGroupSchema(t *testing.T) { func TestSecurityGroupSchema(t *testing.T) {
t.Parallel() t.Parallel()
s := SecurityGroup().Schema s := SecurityGroup()
structure.AssertRequiredArguments(t, s, []string{ test.AssertRequiredArguments(t, s, []string{
mkSecurityGroupName, mkSecurityGroupName,
}) })
structure.AssertOptionalArguments(t, s, []string{ test.AssertOptionalArguments(t, s, []string{
mkSecurityGroupComment, mkSecurityGroupComment,
}) })
structure.AssertValueTypes(t, s, map[string]schema.ValueType{ test.AssertValueTypes(t, s, map[string]schema.ValueType{
mkSecurityGroupName: schema.TypeString, mkSecurityGroupName: schema.TypeString,
mkSecurityGroupComment: schema.TypeString, mkSecurityGroupComment: schema.TypeString,
}) })
structure.AssertNestedSchemaExistence(t, s, firewall.MkRule) test.AssertNestedSchemaExistence(t, s, firewall.MkRule)
} }

View File

@ -21,6 +21,7 @@ import (
"github.com/bpg/terraform-provider-proxmox/proxmox/nodes/containers" "github.com/bpg/terraform-provider-proxmox/proxmox/nodes/containers"
"github.com/bpg/terraform-provider-proxmox/proxmoxtf" "github.com/bpg/terraform-provider-proxmox/proxmoxtf"
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/validator" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/validator"
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure"
) )
const ( const (
@ -571,7 +572,7 @@ func Container() *schema.Resource {
Description: "Volume size (only used for volume mount points)", Description: "Volume size (only used for volume mount points)",
Optional: true, Optional: true,
Default: dvResourceVirtualEnvironmentContainerMountPointSize, Default: dvResourceVirtualEnvironmentContainerMountPointSize,
ValidateDiagFunc: getFileSizeValidator(), ValidateDiagFunc: validator.FileSize(),
}, },
mkResourceVirtualEnvironmentContainerMountPointVolume: { mkResourceVirtualEnvironmentContainerMountPointVolume: {
Type: schema.TypeString, Type: schema.TypeString,
@ -618,7 +619,7 @@ func Container() *schema.Resource {
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
return new == "" return new == ""
}, },
ValidateDiagFunc: getMACAddressValidator(), ValidateDiagFunc: validator.MACAddress(),
}, },
mkResourceVirtualEnvironmentContainerNetworkInterfaceName: { mkResourceVirtualEnvironmentContainerNetworkInterfaceName: {
Type: schema.TypeString, Type: schema.TypeString,
@ -666,7 +667,7 @@ func Container() *schema.Resource {
Description: "The ID of an OS template file", Description: "The ID of an OS template file",
Required: true, Required: true,
ForceNew: true, ForceNew: true,
ValidateDiagFunc: getFileIDValidator(), ValidateDiagFunc: validator.FileID(),
}, },
mkResourceVirtualEnvironmentContainerOperatingSystemType: { mkResourceVirtualEnvironmentContainerOperatingSystemType: {
Type: schema.TypeString, Type: schema.TypeString,
@ -704,7 +705,7 @@ func Container() *schema.Resource {
Type: schema.TypeString, Type: schema.TypeString,
ValidateFunc: validation.StringIsNotEmpty, ValidateFunc: validation.StringIsNotEmpty,
}, },
DiffSuppressFunc: suppressIfListsAreEqualIgnoringOrder, DiffSuppressFunc: structure.SuppressIfListsAreEqualIgnoringOrder,
DiffSuppressOnRefresh: true, DiffSuppressOnRefresh: true,
}, },
mkResourceVirtualEnvironmentContainerTemplate: { mkResourceVirtualEnvironmentContainerTemplate: {
@ -1123,7 +1124,7 @@ func containerCreateCustom(ctx context.Context, d *schema.ResourceData, m interf
nodeName := d.Get(mkResourceVirtualEnvironmentContainerNodeName).(string) nodeName := d.Get(mkResourceVirtualEnvironmentContainerNodeName).(string)
resource := Container() resource := Container()
consoleBlock, err := getSchemaBlock( consoleBlock, err := structure.GetSchemaBlock(
resource, resource,
d, d,
[]string{mkResourceVirtualEnvironmentContainerConsole}, []string{mkResourceVirtualEnvironmentContainerConsole},
@ -1140,7 +1141,7 @@ func containerCreateCustom(ctx context.Context, d *schema.ResourceData, m interf
consoleMode := consoleBlock[mkResourceVirtualEnvironmentContainerConsoleMode].(string) consoleMode := consoleBlock[mkResourceVirtualEnvironmentContainerConsoleMode].(string)
consoleTTYCount := consoleBlock[mkResourceVirtualEnvironmentContainerConsoleTTYCount].(int) consoleTTYCount := consoleBlock[mkResourceVirtualEnvironmentContainerConsoleTTYCount].(int)
cpuBlock, err := getSchemaBlock( cpuBlock, err := structure.GetSchemaBlock(
resource, resource,
d, d,
[]string{mkResourceVirtualEnvironmentContainerCPU}, []string{mkResourceVirtualEnvironmentContainerCPU},
@ -1157,7 +1158,7 @@ func containerCreateCustom(ctx context.Context, d *schema.ResourceData, m interf
description := d.Get(mkResourceVirtualEnvironmentContainerDescription).(string) description := d.Get(mkResourceVirtualEnvironmentContainerDescription).(string)
diskBlock, err := getSchemaBlock( diskBlock, err := structure.GetSchemaBlock(
resource, resource,
d, d,
[]string{mkResourceVirtualEnvironmentContainerDisk}, []string{mkResourceVirtualEnvironmentContainerDisk},
@ -1181,7 +1182,7 @@ func containerCreateCustom(ctx context.Context, d *schema.ResourceData, m interf
} }
} }
featuresBlock, err := getSchemaBlock( featuresBlock, err := structure.GetSchemaBlock(
resource, resource,
d, d,
[]string{mkResourceVirtualEnvironmentContainerFeatures}, []string{mkResourceVirtualEnvironmentContainerFeatures},
@ -1282,7 +1283,7 @@ func containerCreateCustom(ctx context.Context, d *schema.ResourceData, m interf
} }
} }
memoryBlock, err := getSchemaBlock( memoryBlock, err := structure.GetSchemaBlock(
resource, resource,
d, d,
[]string{mkResourceVirtualEnvironmentContainerMemory}, []string{mkResourceVirtualEnvironmentContainerMemory},
@ -2273,7 +2274,7 @@ func containerUpdate(ctx context.Context, d *schema.ResourceData, m interface{})
// Prepare the new console configuration. // Prepare the new console configuration.
if d.HasChange(mkResourceVirtualEnvironmentContainerConsole) { if d.HasChange(mkResourceVirtualEnvironmentContainerConsole) {
consoleBlock, err := getSchemaBlock( consoleBlock, err := structure.GetSchemaBlock(
resource, resource,
d, d,
[]string{mkResourceVirtualEnvironmentContainerConsole}, []string{mkResourceVirtualEnvironmentContainerConsole},
@ -2299,7 +2300,7 @@ func containerUpdate(ctx context.Context, d *schema.ResourceData, m interface{})
// Prepare the new CPU configuration. // Prepare the new CPU configuration.
if d.HasChange(mkResourceVirtualEnvironmentContainerCPU) { if d.HasChange(mkResourceVirtualEnvironmentContainerCPU) {
cpuBlock, err := getSchemaBlock( cpuBlock, err := structure.GetSchemaBlock(
resource, resource,
d, d,
[]string{mkResourceVirtualEnvironmentContainerCPU}, []string{mkResourceVirtualEnvironmentContainerCPU},
@ -2396,7 +2397,7 @@ func containerUpdate(ctx context.Context, d *schema.ResourceData, m interface{})
// Prepare the new memory configuration. // Prepare the new memory configuration.
if d.HasChange(mkResourceVirtualEnvironmentContainerMemory) { if d.HasChange(mkResourceVirtualEnvironmentContainerMemory) {
memoryBlock, err := getSchemaBlock( memoryBlock, err := structure.GetSchemaBlock(
resource, resource,
d, d,
[]string{mkResourceVirtualEnvironmentContainerMemory}, []string{mkResourceVirtualEnvironmentContainerMemory},
@ -2561,7 +2562,7 @@ func containerUpdate(ctx context.Context, d *schema.ResourceData, m interface{})
// Prepare the new operating system configuration. // Prepare the new operating system configuration.
if d.HasChange(mkResourceVirtualEnvironmentContainerOperatingSystem) { if d.HasChange(mkResourceVirtualEnvironmentContainerOperatingSystem) {
operatingSystem, err := getSchemaBlock( operatingSystem, err := structure.GetSchemaBlock(
resource, resource,
d, d,
[]string{mkResourceVirtualEnvironmentContainerOperatingSystem}, []string{mkResourceVirtualEnvironmentContainerOperatingSystem},

View File

@ -29,6 +29,7 @@ import (
"github.com/bpg/terraform-provider-proxmox/proxmox/api" "github.com/bpg/terraform-provider-proxmox/proxmox/api"
"github.com/bpg/terraform-provider-proxmox/proxmoxtf" "github.com/bpg/terraform-provider-proxmox/proxmoxtf"
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/validator"
"github.com/bpg/terraform-provider-proxmox/utils" "github.com/bpg/terraform-provider-proxmox/utils"
) )
@ -69,7 +70,7 @@ func File() *schema.Resource {
Optional: true, Optional: true,
ForceNew: true, ForceNew: true,
Default: dvResourceVirtualEnvironmentFileContentType, Default: dvResourceVirtualEnvironmentFileContentType,
ValidateDiagFunc: getContentTypeValidator(), ValidateDiagFunc: validator.ContentType(),
}, },
mkResourceVirtualEnvironmentFileDatastoreID: { mkResourceVirtualEnvironmentFileDatastoreID: {
Type: schema.TypeString, Type: schema.TypeString,
@ -523,7 +524,7 @@ func fileGetContentType(d *schema.ResourceData) (*string, diag.Diagnostics) {
} }
} }
ctValidator := getContentTypeValidator() ctValidator := validator.ContentType()
diags := ctValidator(contentType, cty.GetAttrPath(mkResourceVirtualEnvironmentFileContentType)) diags := ctValidator(contentType, cty.GetAttrPath(mkResourceVirtualEnvironmentFileContentType))
return &contentType, diags return &contentType, diags

View File

@ -12,7 +12,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test"
) )
// TestAliasInstantiation tests whether the Alias instance can be instantiated. // TestAliasInstantiation tests whether the Alias instance can be instantiated.
@ -25,20 +25,20 @@ func TestAliasInstantiation(t *testing.T) {
func TestAliasSchema(t *testing.T) { func TestAliasSchema(t *testing.T) {
t.Parallel() t.Parallel()
s := Alias().Schema s := Alias()
structure.AssertRequiredArguments(t, s, []string{ test.AssertRequiredArguments(t, s, []string{
mkAliasName, mkAliasName,
mkAliasCIDR, mkAliasCIDR,
}) })
structure.AssertOptionalArguments(t, s, []string{ test.AssertOptionalArguments(t, s, []string{
mkSelectorVMID, mkSelectorVMID,
mkSelectorNodeName, mkSelectorNodeName,
mkAliasComment, mkAliasComment,
}) })
structure.AssertValueTypes(t, s, map[string]schema.ValueType{ test.AssertValueTypes(t, s, map[string]schema.ValueType{
mkAliasName: schema.TypeString, mkAliasName: schema.TypeString,
mkAliasCIDR: schema.TypeString, mkAliasCIDR: schema.TypeString,
mkAliasComment: schema.TypeString, mkAliasComment: schema.TypeString,

View File

@ -12,7 +12,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test"
) )
// TestIPSetInstantiation tests whether the IPSet // TestIPSetInstantiation tests whether the IPSet
@ -26,37 +26,37 @@ func TestIPSetInstantiation(t *testing.T) {
func TestIPSetSchema(t *testing.T) { func TestIPSetSchema(t *testing.T) {
t.Parallel() t.Parallel()
s := IPSet().Schema s := IPSet()
structure.AssertRequiredArguments(t, s, []string{ test.AssertRequiredArguments(t, s, []string{
mkIPSetName, mkIPSetName,
}) })
structure.AssertOptionalArguments(t, s, []string{ test.AssertOptionalArguments(t, s, []string{
mkSelectorVMID, mkSelectorVMID,
mkSelectorNodeName, mkSelectorNodeName,
mkIPSetCIDR, mkIPSetCIDR,
mkIPSetCIDRComment, mkIPSetCIDRComment,
}) })
structure.AssertValueTypes(t, s, map[string]schema.ValueType{ test.AssertValueTypes(t, s, map[string]schema.ValueType{
mkIPSetName: schema.TypeString, mkIPSetName: schema.TypeString,
mkIPSetCIDR: schema.TypeList, mkIPSetCIDR: schema.TypeList,
mkIPSetCIDRComment: schema.TypeString, mkIPSetCIDRComment: schema.TypeString,
}) })
nested := structure.AssertNestedSchemaExistence(t, s, mkIPSetCIDR).Schema nested := test.AssertNestedSchemaExistence(t, s, mkIPSetCIDR)
structure.AssertRequiredArguments(t, nested, []string{ test.AssertRequiredArguments(t, nested, []string{
mkIPSetCIDRName, mkIPSetCIDRName,
}) })
structure.AssertOptionalArguments(t, nested, []string{ test.AssertOptionalArguments(t, nested, []string{
mkIPSetCIDRComment, mkIPSetCIDRComment,
mkIPSetCIDRNoMatch, mkIPSetCIDRNoMatch,
}) })
structure.AssertValueTypes(t, nested, map[string]schema.ValueType{ test.AssertValueTypes(t, nested, map[string]schema.ValueType{
mkIPSetCIDRName: schema.TypeString, mkIPSetCIDRName: schema.TypeString,
mkIPSetCIDRComment: schema.TypeString, mkIPSetCIDRComment: schema.TypeString,
mkIPSetCIDRNoMatch: schema.TypeBool, mkIPSetCIDRNoMatch: schema.TypeBool,

View File

@ -12,7 +12,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test"
) )
// TestOptionsInstantiation tests whether the Options instance can be instantiated. // TestOptionsInstantiation tests whether the Options instance can be instantiated.
@ -25,9 +25,9 @@ func TestOptionsInstantiation(t *testing.T) {
func TestOptionsSchema(t *testing.T) { func TestOptionsSchema(t *testing.T) {
t.Parallel() t.Parallel()
s := Options().Schema s := Options()
structure.AssertOptionalArguments(t, s, []string{ test.AssertOptionalArguments(t, s, []string{
mkDHCP, mkDHCP,
mkEnabled, mkEnabled,
mkIPFilter, mkIPFilter,
@ -40,7 +40,7 @@ func TestOptionsSchema(t *testing.T) {
mkRadv, mkRadv,
}) })
structure.AssertValueTypes(t, s, map[string]schema.ValueType{ test.AssertValueTypes(t, s, map[string]schema.ValueType{
mkDHCP: schema.TypeBool, mkDHCP: schema.TypeBool,
mkEnabled: schema.TypeBool, mkEnabled: schema.TypeBool,
mkIPFilter: schema.TypeBool, mkIPFilter: schema.TypeBool,

View File

@ -12,7 +12,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test"
) )
// TestRuleInstantiation tests whether the Rules instance can be instantiated. // TestRuleInstantiation tests whether the Rules instance can be instantiated.
@ -25,20 +25,20 @@ func TestRuleInstantiation(t *testing.T) {
func TestRuleSchema(t *testing.T) { func TestRuleSchema(t *testing.T) {
t.Parallel() t.Parallel()
rulesSchema := Rules().Schema rules := Rules()
structure.AssertRequiredArguments(t, rulesSchema, []string{ test.AssertRequiredArguments(t, rules, []string{
MkRule, MkRule,
}) })
structure.AssertOptionalArguments(t, rulesSchema, []string{ test.AssertOptionalArguments(t, rules, []string{
mkSelectorVMID, mkSelectorVMID,
mkSelectorNodeName, mkSelectorNodeName,
}) })
ruleSchema := structure.AssertNestedSchemaExistence(t, rulesSchema, MkRule).Schema nested := test.AssertNestedSchemaExistence(t, rules, MkRule)
structure.AssertOptionalArguments(t, ruleSchema, []string{ test.AssertOptionalArguments(t, nested, []string{
mkSecurityGroup, mkSecurityGroup,
mkRuleAction, mkRuleAction,
mkRuleType, mkRuleType,
@ -54,7 +54,7 @@ func TestRuleSchema(t *testing.T) {
mkRuleSPort, mkRuleSPort,
}) })
structure.AssertValueTypes(t, ruleSchema, map[string]schema.ValueType{ test.AssertValueTypes(t, nested, map[string]schema.ValueType{
mkRulePos: schema.TypeInt, mkRulePos: schema.TypeInt,
mkRuleAction: schema.TypeString, mkRuleAction: schema.TypeString,
mkRuleType: schema.TypeString, mkRuleType: schema.TypeString,

View File

@ -1,582 +0,0 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package resource
import (
"fmt"
"reflect"
"regexp"
"sort"
"strings"
"time"
"unicode"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/bpg/terraform-provider-proxmox/internal/types"
"github.com/bpg/terraform-provider-proxmox/proxmox/nodes/vms"
)
func getBIOSValidator() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(validation.StringInSlice([]string{
"ovmf",
"seabios",
}, false))
}
func getContentTypeValidator() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(validation.StringInSlice([]string{
"backup",
"iso",
"snippets",
"vztmpl",
}, false))
}
//nolint:unused
func getCPUFlagsValidator() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(func(i interface{}, k string) (ws []string, es []error) {
list, ok := i.([]interface{})
if !ok {
es = append(es, fmt.Errorf("expected type of %s to be []interface{}", k))
return
}
validator := validation.StringInSlice([]string{
"+aes",
"-aes",
"+amd-no-ssb",
"-amd-no-ssb",
"+amd-ssbd",
"-amd-ssbd",
"+hv-evmcs",
"-hv-evmcs",
"+hv-tlbflush",
"-hv-tlbflush",
"+ibpb",
"-ibpb",
"+md-clear",
"-md-clear",
"+pcid",
"-pcid",
"+pdpe1gb",
"-pdpe1gb",
"+spec-ctrl",
"-spec-ctrl",
"+ssbd",
"-ssbd",
"+virt-ssbd",
"-virt-ssbd",
}, false)
for li, lv := range list {
v, ok := lv.(string)
if !ok {
es = append(es, fmt.Errorf("expected type of %s[%d] to be string", k, li))
return
}
warns, errs := validator(v, k)
ws = append(ws, warns...)
es = append(es, errs...)
if len(es) > 0 {
return
}
}
return
})
}
func getCPUTypeValidator() schema.SchemaValidateDiagFunc {
standardTypes := []string{
"486",
"Broadwell",
"Broadwell-IBRS",
"Broadwell-noTSX",
"Broadwell-noTSX-IBRS",
"Cascadelake-Server",
"Cascadelake-Server-noTSX",
"Cascadelake-Server-v2",
"Cascadelake-Server-v4",
"Cascadelake-Server-v5",
"Conroe",
"Cooperlake",
"Cooperlake-v2",
"EPYC",
"EPYC-IBPB",
"EPYC-Milan",
"EPYC-Rome",
"EPYC-Rome-v2",
"EPYC-v3",
"Haswell",
"Haswell-IBRS",
"Haswell-noTSX",
"Haswell-noTSX-IBRS",
"Icelake-Client",
"Icelake-Client-noTSX",
"Icelake-Server",
"Icelake-Server-noTSX",
"Icelake-Server-v3",
"Icelake-Server-v4",
"Icelake-Server-v5",
"Icelake-Server-v6",
"IvyBridge",
"IvyBridge-IBRS",
"KnightsMill",
"Nehalem",
"Nehalem-IBRS",
"Opteron_G1",
"Opteron_G2",
"Opteron_G3",
"Opteron_G4",
"Opteron_G5",
"Penryn",
"SandyBridge",
"SandyBridge-IBRS",
"SapphireRapids",
"Skylake-Client",
"Skylake-Client-IBRS",
"Skylake-Client-noTSX-IBRS",
"Skylake-Client-v4",
"Skylake-Server",
"Skylake-Server-IBRS",
"Skylake-Server-noTSX-IBRS",
"Skylake-Server-v4",
"Skylake-Server-v5",
"Westmere",
"Westmere-IBRS",
"athlon",
"core2duo",
"coreduo",
"host",
"kvm32",
"kvm64",
"max",
"pentium",
"pentium2",
"pentium3",
"phenom",
"qemu32",
"qemu64",
"x86-64-v2",
"x86-64-v2-AES",
"x86-64-v3",
"x86-64-v4",
}
return validation.ToDiagFunc(validation.Any(
validation.StringInSlice(standardTypes, false),
validation.StringMatch(regexp.MustCompile(`^custom-.+$`), "must be a valid custom CPU type"),
))
}
func getFileFormatValidator() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(validation.StringInSlice([]string{
"qcow2",
"raw",
"vmdk",
}, false))
}
func getFileIDValidator() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(func(i interface{}, k string) (ws []string, es []error) {
v, ok := i.(string)
if !ok {
es = append(es, fmt.Errorf("expected type of %s to be string", k))
return
}
if v != "" {
r := regexp.MustCompile(`^(?i)[a-z\d\-_]+:([a-z\d\-_]+/)?.+$`)
ok := r.MatchString(v)
if !ok {
es = append(es, fmt.Errorf("expected %s to be a valid file identifier (datastore-name:iso/some-file.img), got %s", k, v))
return
}
}
return
})
}
func getFileSizeValidator() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(func(i interface{}, k string) ([]string, []error) {
v, ok := i.(string)
var es []error
if !ok {
es = append(es, fmt.Errorf("expected type of %s to be string", k))
return nil, es
}
if v != "" {
_, err := types.ParseDiskSize(v)
if err != nil {
es = append(es, fmt.Errorf("expected %s to be a valid file size (100, 1M, 1G), got %s", k, v))
return nil, es
}
}
return []string{}, es
})
}
func getKeyboardLayoutValidator() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(validation.StringInSlice([]string{
"da",
"de",
"de-ch",
"en-gb",
"en-us",
"es",
"fi",
"fr",
"fr-be",
"fr-ca",
"fr-ch",
"hu",
"is",
"it",
"ja",
"lt",
"mk",
"nl",
"no",
"pl",
"pt",
"pt-br",
"sl",
"sv",
"tr",
}, false))
}
func diskDigitPrefix(s string) string {
for i, r := range s {
if unicode.IsDigit(r) {
return s[:i]
}
}
return s
}
func getMACAddressValidator() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(func(i interface{}, k string) (ws []string, es []error) {
v, ok := i.(string)
if !ok {
es = append(es, fmt.Errorf("expected type of %s to be string", k))
return
}
if v != "" {
r := regexp.MustCompile(`^[A-Z\d]{2}(:[A-Z\d]{2}){5}$`)
ok := r.MatchString(v)
if !ok {
es = append(es, fmt.Errorf("expected %s to be a valid MAC address (A0:B1:C2:D3:E4:F5), got %s", k, v))
return
}
}
return
})
}
func getNetworkDeviceModelValidator() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(validation.StringInSlice([]string{"e1000", "rtl8139", "virtio", "vmxnet3"}, false))
}
func getQEMUAgentTypeValidator() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(validation.StringInSlice([]string{"isa", "virtio"}, false))
}
func getSchemaBlock(r *schema.Resource, d *schema.ResourceData, k []string, i int, allowDefault bool) (map[string]interface{}, error) {
var resourceBlock map[string]interface{}
var resourceData interface{}
var resourceSchema *schema.Schema
for ki, kv := range k {
if ki == 0 {
resourceData = d.Get(kv)
resourceSchema = r.Schema[kv]
} else {
mapValues := resourceData.([]interface{})
if len(mapValues) <= i {
return resourceBlock, fmt.Errorf("index out of bounds %d", i)
}
mapValue := mapValues[i].(map[string]interface{})
resourceData = mapValue[kv]
resourceSchema = resourceSchema.Elem.(*schema.Resource).Schema[kv]
}
}
list := resourceData.([]interface{})
if len(list) == 0 {
if allowDefault {
listDefault, err := resourceSchema.DefaultValue()
if err != nil {
return nil, err
}
list = listDefault.([]interface{})
}
}
if len(list) > i {
resourceBlock = list[i].(map[string]interface{})
}
return resourceBlock, nil
}
func getTimeoutValidator() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(func(i interface{}, k string) (ws []string, es []error) {
v, ok := i.(string)
if !ok {
es = append(es, fmt.Errorf("expected type of %s to be string", k))
return
}
_, err := time.ParseDuration(v)
if err != nil {
es = append(es, fmt.Errorf("expected value of %s to be a duration - got: %s", k, v))
return
}
return
})
}
func getVGAMemoryValidator() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(validation.IntBetween(4, 512))
}
func getVGATypeValidator() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(validation.StringInSlice([]string{
"cirrus",
"qxl",
"qxl2",
"qxl3",
"qxl4",
"serial0",
"serial1",
"serial2",
"serial3",
"std",
"virtio",
"vmware",
}, false))
}
func getSCSIHardwareValidator() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(validation.StringInSlice([]string{
"lsi",
"lsi53c810",
"virtio-scsi-pci",
"virtio-scsi-single",
"megasas",
"pvscsi",
}, false))
}
func getIDEInterfaceValidator() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(validation.StringInSlice([]string{
"ide0",
"ide1",
"ide2",
"ide3",
}, false))
}
// suppressIfListsAreEqualIgnoringOrder is a customdiff.SuppressionFunc that suppresses
// changes to a list if the old and new lists are equal, ignoring the order of the
// elements.
// It will be called for each list item, so it is not super efficient. It is
// recommended to use it only for small lists.
// Ref: https://github.com/hashicorp/terraform-plugin-sdk/issues/477
func suppressIfListsAreEqualIgnoringOrder(key, _, _ string, d *schema.ResourceData) bool {
// the key is a path to the list item, not the list itself, e.g. "tags.0"
lastDotIndex := strings.LastIndex(key, ".")
if lastDotIndex != -1 {
key = key[:lastDotIndex]
}
oldData, newData := d.GetChange(key)
if oldData == nil || newData == nil {
return false
}
oldArray := oldData.([]interface{})
newArray := newData.([]interface{})
if len(oldArray) != len(newArray) {
return false
}
oldEvents := make([]string, len(oldArray))
newEvents := make([]string, len(newArray))
for i, oldEvt := range oldArray {
oldEvents[i] = fmt.Sprint(oldEvt)
}
for j, newEvt := range newArray {
newEvents[j] = fmt.Sprint(newEvt)
}
sort.Strings(oldEvents)
sort.Strings(newEvents)
return reflect.DeepEqual(oldEvents, newEvents)
}
func getDiskInfo(resp *vms.GetResponseData, d *schema.ResourceData) map[string]*vms.CustomStorageDevice {
currentDisk := d.Get(mkResourceVirtualEnvironmentVMDisk)
currentDiskList := currentDisk.([]interface{})
currentDiskMap := map[string]map[string]interface{}{}
for _, v := range currentDiskList {
diskMap := v.(map[string]interface{})
diskInterface := diskMap[mkResourceVirtualEnvironmentVMDiskInterface].(string)
currentDiskMap[diskInterface] = diskMap
}
storageDevices := map[string]*vms.CustomStorageDevice{}
storageDevices["ide0"] = resp.IDEDevice0
storageDevices["ide1"] = resp.IDEDevice1
storageDevices["ide2"] = resp.IDEDevice2
storageDevices["ide3"] = resp.IDEDevice3
storageDevices["sata0"] = resp.SATADevice0
storageDevices["sata1"] = resp.SATADevice1
storageDevices["sata2"] = resp.SATADevice2
storageDevices["sata3"] = resp.SATADevice3
storageDevices["sata4"] = resp.SATADevice4
storageDevices["sata5"] = resp.SATADevice5
storageDevices["scsi0"] = resp.SCSIDevice0
storageDevices["scsi1"] = resp.SCSIDevice1
storageDevices["scsi2"] = resp.SCSIDevice2
storageDevices["scsi3"] = resp.SCSIDevice3
storageDevices["scsi4"] = resp.SCSIDevice4
storageDevices["scsi5"] = resp.SCSIDevice5
storageDevices["scsi6"] = resp.SCSIDevice6
storageDevices["scsi7"] = resp.SCSIDevice7
storageDevices["scsi8"] = resp.SCSIDevice8
storageDevices["scsi9"] = resp.SCSIDevice9
storageDevices["scsi10"] = resp.SCSIDevice10
storageDevices["scsi11"] = resp.SCSIDevice11
storageDevices["scsi12"] = resp.SCSIDevice12
storageDevices["scsi13"] = resp.SCSIDevice13
storageDevices["virtio0"] = resp.VirtualIODevice0
storageDevices["virtio1"] = resp.VirtualIODevice1
storageDevices["virtio2"] = resp.VirtualIODevice2
storageDevices["virtio3"] = resp.VirtualIODevice3
storageDevices["virtio4"] = resp.VirtualIODevice4
storageDevices["virtio5"] = resp.VirtualIODevice5
storageDevices["virtio6"] = resp.VirtualIODevice6
storageDevices["virtio7"] = resp.VirtualIODevice7
storageDevices["virtio8"] = resp.VirtualIODevice8
storageDevices["virtio9"] = resp.VirtualIODevice9
storageDevices["virtio10"] = resp.VirtualIODevice10
storageDevices["virtio11"] = resp.VirtualIODevice11
storageDevices["virtio12"] = resp.VirtualIODevice12
storageDevices["virtio13"] = resp.VirtualIODevice13
storageDevices["virtio14"] = resp.VirtualIODevice14
storageDevices["virtio15"] = resp.VirtualIODevice15
for k, v := range storageDevices {
if v != nil {
if currentDiskMap[k] != nil {
if currentDiskMap[k][mkResourceVirtualEnvironmentVMDiskFileID] != nil {
fileID := currentDiskMap[k][mkResourceVirtualEnvironmentVMDiskFileID].(string)
v.FileID = &fileID
}
}
// defensive copy of the loop variable
iface := k
v.Interface = &iface
}
}
return storageDevices
}
// getDiskDatastores returns a list of the used datastores in a VM
func getDiskDatastores(vm *vms.GetResponseData, d *schema.ResourceData) []string {
storageDevices := getDiskInfo(vm, d)
datastoresSet := map[string]int{}
for _, diskInfo := range storageDevices {
// Ignore empty storage devices and storage devices (like ide) which may not have any media mounted
if diskInfo == nil || diskInfo.FileVolume == "none" {
continue
}
fileIDParts := strings.Split(diskInfo.FileVolume, ":")
datastoresSet[fileIDParts[0]] = 1
}
if vm.EFIDisk != nil {
fileIDParts := strings.Split(vm.EFIDisk.FileVolume, ":")
datastoresSet[fileIDParts[0]] = 1
}
datastores := []string{}
for datastore := range datastoresSet {
datastores = append(datastores, datastore)
}
return datastores
}
func getPCIInfo(resp *vms.GetResponseData, _ *schema.ResourceData) map[string]*vms.CustomPCIDevice {
pciDevices := map[string]*vms.CustomPCIDevice{}
pciDevices["hostpci0"] = resp.PCIDevice0
pciDevices["hostpci1"] = resp.PCIDevice1
pciDevices["hostpci2"] = resp.PCIDevice2
pciDevices["hostpci3"] = resp.PCIDevice3
return pciDevices
}
func getCloudInitTypeValidator() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(validation.StringInSlice([]string{
"configdrive2",
"nocloud",
}, false))
}
func parseImportIDWithNodeName(id string) (string, string, error) {
nodeName, id, found := strings.Cut(id, "/")
if !found {
return "", "", fmt.Errorf("unexpected format of ID (%s), expected node/id", id)
}
return nodeName, id, nil
}

View File

@ -0,0 +1,78 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package validator
import (
"fmt"
"regexp"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/bpg/terraform-provider-proxmox/internal/types"
)
// FileFormat returns a schema validation function for a file format.
func FileFormat() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(validation.StringInSlice([]string{
"qcow2",
"raw",
"vmdk",
}, false))
}
// FileID returns a schema validation function for a file identifier.
func FileID() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(func(i interface{}, k string) ([]string, []error) {
v, ok := i.(string)
var ws []string
var es []error
if !ok {
es = append(es, fmt.Errorf("expected type of %s to be string", k))
return ws, es
}
if v != "" {
r := regexp.MustCompile(`^(?i)[a-z\d\-_]+:([a-z\d\-_]+/)?.+$`)
ok := r.MatchString(v)
if !ok {
es = append(es, fmt.Errorf(
"expected %s to be a valid file identifier (datastore-name:iso/some-file.img), got %s", k, v,
))
return ws, es
}
}
return ws, es
})
}
// FileSize is a schema validation function for file size.
func FileSize() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(func(i interface{}, k string) ([]string, []error) {
v, ok := i.(string)
var es []error
if !ok {
es = append(es, fmt.Errorf("expected type of %s to be string", k))
return nil, es
}
if v != "" {
_, err := types.ParseDiskSize(v)
if err != nil {
es = append(es, fmt.Errorf("expected %s to be a valid file size (100, 1M, 1G), got %s", k, v))
return nil, es
}
}
return []string{}, es
})
}

View File

@ -8,69 +8,35 @@ package validator
import ( import (
"fmt" "fmt"
"regexp"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
) )
// VLANIDsValidator returns a schema validation function for VLAN IDs. // MACAddress is a schema validation function for MAC address.
func VLANIDsValidator() schema.SchemaValidateDiagFunc { func MACAddress() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(func(i interface{}, k string) ([]string, []error) { return validation.ToDiagFunc(func(i interface{}, k string) ([]string, []error) {
min := 1 v, ok := i.(string)
max := 4094
var ws []string var ws []string
var es []error var es []error
list, ok := i.([]interface{})
if !ok { if !ok {
es = append(es, fmt.Errorf("expected type of %s to be []interface{}", k)) es = append(es, fmt.Errorf("expected type of %s to be string", k))
return ws, es return ws, es
} }
for li, lv := range list { if v != "" {
v, ok := lv.(int) r := regexp.MustCompile(`^[A-Z\d]{2}(:[A-Z\d]{2}){5}$`)
ok := r.MatchString(v)
if !ok { if !ok {
es = append(es, fmt.Errorf("expected type of %s[%d] to be int", k, li)) es = append(es, fmt.Errorf("expected %s to be a valid MAC address (A0:B1:C2:D3:E4:F5), got %s", k, v))
return ws, es return ws, es
} }
if v != -1 {
if v < min || v > max {
es = append(es, fmt.Errorf("expected %s[%d] to be in the range (%d - %d), got %d", k, li, min, max, v))
return ws, es
}
}
} }
return ws, es return ws, es
}) })
} }
// NodeNetworkInterfaceBondingModes returns a schema validation function for a node network interface bonding mode.
func NodeNetworkInterfaceBondingModes() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(validation.StringInSlice([]string{
"balance-rr",
"active-backup",
"balance-xor",
"broadcast",
"802.3ad",
"balance-tlb",
"balance-alb",
"balance-slb",
"lacp-balance-slb",
"lacp-balance-tcp",
}, false))
}
// NodeNetworkInterfaceBondingTransmitHashPolicies returns a schema validation function for a node network interface
// bonding transmit hash policy.
func NodeNetworkInterfaceBondingTransmitHashPolicies() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(validation.StringInSlice([]string{
"layer2",
"layer2+3",
"layer3+4",
}, false))
}

View File

@ -8,6 +8,8 @@ package validator
import ( import (
"fmt" "fmt"
"regexp"
"time"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
@ -39,3 +41,222 @@ func VMID() schema.SchemaValidateDiagFunc {
return ws, es return ws, es
}) })
} }
// BIOS returns a schema validation function for a BIOS type.
func BIOS() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(validation.StringInSlice([]string{
"ovmf",
"seabios",
}, false))
}
// ContentType returns a schema validation function for a content type on a storage device.
func ContentType() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(validation.StringInSlice([]string{
"backup",
"iso",
"snippets",
"vztmpl",
}, false))
}
// CPUType returns a schema validation function for a CPU type.
func CPUType() schema.SchemaValidateDiagFunc {
standardTypes := []string{
"486",
"Broadwell",
"Broadwell-IBRS",
"Broadwell-noTSX",
"Broadwell-noTSX-IBRS",
"Cascadelake-Server",
"Cascadelake-Server-noTSX",
"Cascadelake-Server-v2",
"Cascadelake-Server-v4",
"Cascadelake-Server-v5",
"Conroe",
"Cooperlake",
"Cooperlake-v2",
"EPYC",
"EPYC-IBPB",
"EPYC-Milan",
"EPYC-Rome",
"EPYC-Rome-v2",
"EPYC-v3",
"Haswell",
"Haswell-IBRS",
"Haswell-noTSX",
"Haswell-noTSX-IBRS",
"Icelake-Client",
"Icelake-Client-noTSX",
"Icelake-Server",
"Icelake-Server-noTSX",
"Icelake-Server-v3",
"Icelake-Server-v4",
"Icelake-Server-v5",
"Icelake-Server-v6",
"IvyBridge",
"IvyBridge-IBRS",
"KnightsMill",
"Nehalem",
"Nehalem-IBRS",
"Opteron_G1",
"Opteron_G2",
"Opteron_G3",
"Opteron_G4",
"Opteron_G5",
"Penryn",
"SandyBridge",
"SandyBridge-IBRS",
"SapphireRapids",
"Skylake-Client",
"Skylake-Client-IBRS",
"Skylake-Client-noTSX-IBRS",
"Skylake-Client-v4",
"Skylake-Server",
"Skylake-Server-IBRS",
"Skylake-Server-noTSX-IBRS",
"Skylake-Server-v4",
"Skylake-Server-v5",
"Westmere",
"Westmere-IBRS",
"athlon",
"core2duo",
"coreduo",
"host",
"kvm32",
"kvm64",
"max",
"pentium",
"pentium2",
"pentium3",
"phenom",
"qemu32",
"qemu64",
"x86-64-v2",
"x86-64-v2-AES",
"x86-64-v3",
"x86-64-v4",
}
return validation.ToDiagFunc(validation.Any(
validation.StringInSlice(standardTypes, false),
validation.StringMatch(regexp.MustCompile(`^custom-.+$`), "must be a valid custom CPU type"),
))
}
// NetworkDeviceModel is a schema validation function for network device models.
func NetworkDeviceModel() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(validation.StringInSlice([]string{"e1000", "rtl8139", "virtio", "vmxnet3"}, false))
}
// QEMUAgentType is a schema validation function for QEMU agent types.
func QEMUAgentType() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(validation.StringInSlice([]string{"isa", "virtio"}, false))
}
// KeyboardLayout is a schema validation function for keyboard layouts.
func KeyboardLayout() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(validation.StringInSlice([]string{
"da",
"de",
"de-ch",
"en-gb",
"en-us",
"es",
"fi",
"fr",
"fr-be",
"fr-ca",
"fr-ch",
"hu",
"is",
"it",
"ja",
"lt",
"mk",
"nl",
"no",
"pl",
"pt",
"pt-br",
"sl",
"sv",
"tr",
}, false))
}
// Timeout is a schema validation function for timeouts.
func Timeout() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(func(i interface{}, k string) ([]string, []error) {
v, ok := i.(string)
var ws []string
var es []error
if !ok {
es = append(es, fmt.Errorf("expected type of %s to be string", k))
return ws, es
}
_, err := time.ParseDuration(v)
if err != nil {
es = append(es, fmt.Errorf("expected value of %s to be a duration - got: %s", k, v))
return ws, es
}
return ws, es
})
}
// VGAMemory is a schema validation function for VGA memory sizes.
func VGAMemory() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(validation.IntBetween(4, 512))
}
// VGAType is a schema validation function for VGA device types.
func VGAType() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(validation.StringInSlice([]string{
"cirrus",
"qxl",
"qxl2",
"qxl3",
"qxl4",
"serial0",
"serial1",
"serial2",
"serial3",
"std",
"virtio",
"vmware",
}, false))
}
// SCSIHardware is a schema validation function for SCSI hardware.
func SCSIHardware() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(validation.StringInSlice([]string{
"lsi",
"lsi53c810",
"virtio-scsi-pci",
"virtio-scsi-single",
"megasas",
"pvscsi",
}, false))
}
// IDEInterface is a schema validation function for IDE interfaces.
func IDEInterface() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(validation.StringInSlice([]string{
"ide0",
"ide1",
"ide2",
"ide3",
}, false))
}
// CloudInitType is a schema validation function for cloud-init types.
func CloudInitType() schema.SchemaValidateDiagFunc {
return validation.ToDiagFunc(validation.StringInSlice([]string{
"configdrive2",
"nocloud",
}, false))
}

View File

@ -4,7 +4,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/ */
package resource package validator
import ( import (
"testing" "testing"
@ -33,7 +33,7 @@ func Test_getCPUTypeValidator(t *testing.T) {
t.Parallel() t.Parallel()
require := require.New(t) require := require.New(t)
f := getCPUTypeValidator() f := CPUType()
res := f(tt.value, nil) res := f(tt.value, nil)
if tt.valid { if tt.valid {
@ -44,38 +44,3 @@ func Test_getCPUTypeValidator(t *testing.T) {
}) })
} }
} }
func Test_parseImportIDWIthNodeName(t *testing.T) {
t.Parallel()
tests := []struct {
name string
value string
valid bool
expectedNodeName string
expectedID string
}{
{"empty", "", false, "", ""},
{"missing slash", "invalid", false, "", ""},
{"valid", "host/id", true, "host", "id"},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
require := require.New(t)
nodeName, id, err := parseImportIDWithNodeName(tt.value)
if !tt.valid {
require.Error(err)
return
}
require.Nil(err)
require.Equal(tt.expectedNodeName, nodeName)
require.Equal(tt.expectedID, id)
})
}
}

View File

@ -14,6 +14,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"time" "time"
"unicode"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
@ -25,6 +26,7 @@ import (
"github.com/bpg/terraform-provider-proxmox/proxmox/nodes/vms" "github.com/bpg/terraform-provider-proxmox/proxmox/nodes/vms"
"github.com/bpg/terraform-provider-proxmox/proxmoxtf" "github.com/bpg/terraform-provider-proxmox/proxmoxtf"
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/validator" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/resource/validator"
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/structure"
) )
const ( const (
@ -305,7 +307,7 @@ func VM() *schema.Resource {
Description: "The maximum amount of time to wait for data from the QEMU agent to become available", Description: "The maximum amount of time to wait for data from the QEMU agent to become available",
Optional: true, Optional: true,
Default: dvResourceVirtualEnvironmentVMAgentTimeout, Default: dvResourceVirtualEnvironmentVMAgentTimeout,
ValidateDiagFunc: getTimeoutValidator(), ValidateDiagFunc: validator.Timeout(),
}, },
mkResourceVirtualEnvironmentVMAgentTrim: { mkResourceVirtualEnvironmentVMAgentTrim: {
Type: schema.TypeBool, Type: schema.TypeBool,
@ -318,7 +320,7 @@ func VM() *schema.Resource {
Description: "The QEMU agent interface type", Description: "The QEMU agent interface type",
Optional: true, Optional: true,
Default: dvResourceVirtualEnvironmentVMAgentType, Default: dvResourceVirtualEnvironmentVMAgentType,
ValidateDiagFunc: getQEMUAgentTypeValidator(), ValidateDiagFunc: validator.QEMUAgentType(),
}, },
}, },
}, },
@ -370,7 +372,7 @@ func VM() *schema.Resource {
Description: "The BIOS implementation", Description: "The BIOS implementation",
Optional: true, Optional: true,
Default: dvResourceVirtualEnvironmentVMBIOS, Default: dvResourceVirtualEnvironmentVMBIOS,
ValidateDiagFunc: getBIOSValidator(), ValidateDiagFunc: validator.BIOS(),
}, },
mkResourceVirtualEnvironmentVMCDROM: { mkResourceVirtualEnvironmentVMCDROM: {
Type: schema.TypeList, Type: schema.TypeList,
@ -398,14 +400,14 @@ func VM() *schema.Resource {
Description: "The file id", Description: "The file id",
Optional: true, Optional: true,
Default: dvResourceVirtualEnvironmentVMCDROMFileID, Default: dvResourceVirtualEnvironmentVMCDROMFileID,
ValidateDiagFunc: getFileIDValidator(), ValidateDiagFunc: validator.FileID(),
}, },
mkResourceVirtualEnvironmentVMCDROMInterface: { mkResourceVirtualEnvironmentVMCDROMInterface: {
Type: schema.TypeString, Type: schema.TypeString,
Description: "The CDROM interface", Description: "The CDROM interface",
Optional: true, Optional: true,
Default: dvResourceVirtualEnvironmentVMCDROMInterface, Default: dvResourceVirtualEnvironmentVMCDROMInterface,
ValidateDiagFunc: getIDEInterfaceValidator(), ValidateDiagFunc: validator.IDEInterface(),
}, },
}, },
}, },
@ -529,7 +531,7 @@ func VM() *schema.Resource {
Description: "The emulated CPU type", Description: "The emulated CPU type",
Optional: true, Optional: true,
Default: dvResourceVirtualEnvironmentVMCPUType, Default: dvResourceVirtualEnvironmentVMCPUType,
ValidateDiagFunc: getCPUTypeValidator(), ValidateDiagFunc: validator.CPUType(),
}, },
mkResourceVirtualEnvironmentVMCPUUnits: { mkResourceVirtualEnvironmentVMCPUUnits: {
Type: schema.TypeInt, Type: schema.TypeInt,
@ -588,7 +590,7 @@ func VM() *schema.Resource {
Optional: true, Optional: true,
ForceNew: true, ForceNew: true,
Computed: true, Computed: true,
ValidateDiagFunc: getFileFormatValidator(), ValidateDiagFunc: validator.FileFormat(),
}, },
mkResourceVirtualEnvironmentVMDiskFileID: { mkResourceVirtualEnvironmentVMDiskFileID: {
Type: schema.TypeString, Type: schema.TypeString,
@ -596,7 +598,7 @@ func VM() *schema.Resource {
Optional: true, Optional: true,
ForceNew: true, ForceNew: true,
Default: dvResourceVirtualEnvironmentVMDiskFileID, Default: dvResourceVirtualEnvironmentVMDiskFileID,
ValidateDiagFunc: getFileIDValidator(), ValidateDiagFunc: validator.FileID(),
}, },
mkResourceVirtualEnvironmentVMDiskSize: { mkResourceVirtualEnvironmentVMDiskSize: {
Type: schema.TypeInt, Type: schema.TypeInt,
@ -695,7 +697,7 @@ func VM() *schema.Resource {
Optional: true, Optional: true,
ForceNew: true, ForceNew: true,
Computed: true, Computed: true,
ValidateDiagFunc: getFileFormatValidator(), ValidateDiagFunc: validator.FileFormat(),
}, },
mkResourceVirtualEnvironmentVMEFIDiskType: { mkResourceVirtualEnvironmentVMEFIDiskType: {
Type: schema.TypeString, Type: schema.TypeString,
@ -875,7 +877,7 @@ func VM() *schema.Resource {
Optional: true, Optional: true,
ForceNew: true, ForceNew: true,
Default: dvResourceVirtualEnvironmentVMInitializationUserDataFileID, Default: dvResourceVirtualEnvironmentVMInitializationUserDataFileID,
ValidateDiagFunc: getFileIDValidator(), ValidateDiagFunc: validator.FileID(),
}, },
mkResourceVirtualEnvironmentVMInitializationVendorDataFileID: { mkResourceVirtualEnvironmentVMInitializationVendorDataFileID: {
Type: schema.TypeString, Type: schema.TypeString,
@ -883,7 +885,7 @@ func VM() *schema.Resource {
Optional: true, Optional: true,
ForceNew: true, ForceNew: true,
Default: dvResourceVirtualEnvironmentVMInitializationVendorDataFileID, Default: dvResourceVirtualEnvironmentVMInitializationVendorDataFileID,
ValidateDiagFunc: getFileIDValidator(), ValidateDiagFunc: validator.FileID(),
}, },
mkResourceVirtualEnvironmentVMInitializationNetworkDataFileID: { mkResourceVirtualEnvironmentVMInitializationNetworkDataFileID: {
Type: schema.TypeString, Type: schema.TypeString,
@ -891,7 +893,7 @@ func VM() *schema.Resource {
Optional: true, Optional: true,
ForceNew: true, ForceNew: true,
Default: dvResourceVirtualEnvironmentVMInitializationNetworkDataFileID, Default: dvResourceVirtualEnvironmentVMInitializationNetworkDataFileID,
ValidateDiagFunc: getFileIDValidator(), ValidateDiagFunc: validator.FileID(),
}, },
mkResourceVirtualEnvironmentVMInitializationMetaDataFileID: { mkResourceVirtualEnvironmentVMInitializationMetaDataFileID: {
Type: schema.TypeString, Type: schema.TypeString,
@ -899,7 +901,7 @@ func VM() *schema.Resource {
Optional: true, Optional: true,
ForceNew: true, ForceNew: true,
Default: dvResourceVirtualEnvironmentVMInitializationMetaDataFileID, Default: dvResourceVirtualEnvironmentVMInitializationMetaDataFileID,
ValidateDiagFunc: getFileIDValidator(), ValidateDiagFunc: validator.FileID(),
}, },
mkResourceVirtualEnvironmentVMInitializationType: { mkResourceVirtualEnvironmentVMInitializationType: {
Type: schema.TypeString, Type: schema.TypeString,
@ -907,7 +909,7 @@ func VM() *schema.Resource {
Optional: true, Optional: true,
ForceNew: true, ForceNew: true,
Default: dvResourceVirtualEnvironmentVMInitializationType, Default: dvResourceVirtualEnvironmentVMInitializationType,
ValidateDiagFunc: getCloudInitTypeValidator(), ValidateDiagFunc: validator.CloudInitType(),
}, },
}, },
}, },
@ -985,7 +987,7 @@ func VM() *schema.Resource {
Description: "The keyboard layout", Description: "The keyboard layout",
Optional: true, Optional: true,
Default: dvResourceVirtualEnvironmentVMKeyboardLayout, Default: dvResourceVirtualEnvironmentVMKeyboardLayout,
ValidateDiagFunc: getKeyboardLayoutValidator(), ValidateDiagFunc: validator.KeyboardLayout(),
}, },
mkResourceVirtualEnvironmentVMMachine: { mkResourceVirtualEnvironmentVMMachine: {
Type: schema.TypeString, Type: schema.TypeString,
@ -1084,14 +1086,14 @@ func VM() *schema.Resource {
Description: "The MAC address", Description: "The MAC address",
Optional: true, Optional: true,
Computed: true, Computed: true,
ValidateDiagFunc: getMACAddressValidator(), ValidateDiagFunc: validator.MACAddress(),
}, },
mkResourceVirtualEnvironmentVMNetworkDeviceModel: { mkResourceVirtualEnvironmentVMNetworkDeviceModel: {
Type: schema.TypeString, Type: schema.TypeString,
Description: "The model", Description: "The model",
Optional: true, Optional: true,
Default: dvResourceVirtualEnvironmentVMNetworkDeviceModel, Default: dvResourceVirtualEnvironmentVMNetworkDeviceModel,
ValidateDiagFunc: getNetworkDeviceModelValidator(), ValidateDiagFunc: validator.NetworkDeviceModel(),
}, },
mkResourceVirtualEnvironmentVMNetworkDeviceRateLimit: { mkResourceVirtualEnvironmentVMNetworkDeviceRateLimit: {
Type: schema.TypeFloat, Type: schema.TypeFloat,
@ -1208,7 +1210,7 @@ func VM() *schema.Resource {
Type: schema.TypeString, Type: schema.TypeString,
ValidateFunc: validation.StringIsNotEmpty, ValidateFunc: validation.StringIsNotEmpty,
}, },
DiffSuppressFunc: suppressIfListsAreEqualIgnoringOrder, DiffSuppressFunc: structure.SuppressIfListsAreEqualIgnoringOrder,
DiffSuppressOnRefresh: true, DiffSuppressOnRefresh: true,
}, },
mkResourceVirtualEnvironmentVMTemplate: { mkResourceVirtualEnvironmentVMTemplate: {
@ -1280,14 +1282,14 @@ func VM() *schema.Resource {
Description: "The VGA memory in megabytes (4-512 MB)", Description: "The VGA memory in megabytes (4-512 MB)",
Optional: true, Optional: true,
Default: dvResourceVirtualEnvironmentVMVGAMemory, Default: dvResourceVirtualEnvironmentVMVGAMemory,
ValidateDiagFunc: getVGAMemoryValidator(), ValidateDiagFunc: validator.VGAMemory(),
}, },
mkResourceVirtualEnvironmentVMVGAType: { mkResourceVirtualEnvironmentVMVGAType: {
Type: schema.TypeString, Type: schema.TypeString,
Description: "The VGA type", Description: "The VGA type",
Optional: true, Optional: true,
Default: dvResourceVirtualEnvironmentVMVGAType, Default: dvResourceVirtualEnvironmentVMVGAType,
ValidateDiagFunc: getVGATypeValidator(), ValidateDiagFunc: validator.VGAType(),
}, },
}, },
}, },
@ -1308,7 +1310,7 @@ func VM() *schema.Resource {
Description: "The SCSI hardware type", Description: "The SCSI hardware type",
Optional: true, Optional: true,
Default: dvResourceVirtualEnvironmentVMSCSIHardware, Default: dvResourceVirtualEnvironmentVMSCSIHardware,
ValidateDiagFunc: getSCSIHardwareValidator(), ValidateDiagFunc: validator.SCSIHardware(),
}, },
}, },
CreateContext: vmCreate, CreateContext: vmCreate,
@ -1975,7 +1977,7 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
acpi := types.CustomBool(d.Get(mkResourceVirtualEnvironmentVMACPI).(bool)) acpi := types.CustomBool(d.Get(mkResourceVirtualEnvironmentVMACPI).(bool))
agentBlock, err := getSchemaBlock( agentBlock, err := structure.GetSchemaBlock(
resource, resource,
d, d,
[]string{mkResourceVirtualEnvironmentVMAgent}, []string{mkResourceVirtualEnvironmentVMAgent},
@ -1998,7 +2000,7 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
bios := d.Get(mkResourceVirtualEnvironmentVMBIOS).(string) bios := d.Get(mkResourceVirtualEnvironmentVMBIOS).(string)
cdromBlock, err := getSchemaBlock( cdromBlock, err := structure.GetSchemaBlock(
resource, resource,
d, d,
[]string{mkResourceVirtualEnvironmentVMCDROM}, []string{mkResourceVirtualEnvironmentVMCDROM},
@ -2020,7 +2022,7 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
cdromFileID = "cdrom" cdromFileID = "cdrom"
} }
cpuBlock, err := getSchemaBlock( cpuBlock, err := structure.GetSchemaBlock(
resource, resource,
d, d,
[]string{mkResourceVirtualEnvironmentVMCPU}, []string{mkResourceVirtualEnvironmentVMCPU},
@ -2088,7 +2090,7 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
pciDeviceObjects := vmGetHostPCIDeviceObjects(d) pciDeviceObjects := vmGetHostPCIDeviceObjects(d)
keyboardLayout := d.Get(mkResourceVirtualEnvironmentVMKeyboardLayout).(string) keyboardLayout := d.Get(mkResourceVirtualEnvironmentVMKeyboardLayout).(string)
memoryBlock, err := getSchemaBlock( memoryBlock, err := structure.GetSchemaBlock(
resource, resource,
d, d,
[]string{mkResourceVirtualEnvironmentVMMemory}, []string{mkResourceVirtualEnvironmentVMMemory},
@ -2111,7 +2113,7 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
nodeName := d.Get(mkResourceVirtualEnvironmentVMNodeName).(string) nodeName := d.Get(mkResourceVirtualEnvironmentVMNodeName).(string)
operatingSystem, err := getSchemaBlock( operatingSystem, err := structure.GetSchemaBlock(
resource, resource,
d, d,
[]string{mkResourceVirtualEnvironmentVMOperatingSystem}, []string{mkResourceVirtualEnvironmentVMOperatingSystem},
@ -2721,7 +2723,7 @@ func vmGetDiskDeviceObjects(
ssd := types.CustomBool(block[mkResourceVirtualEnvironmentVMDiskSSD].(bool)) ssd := types.CustomBool(block[mkResourceVirtualEnvironmentVMDiskSSD].(bool))
discard := block[mkResourceVirtualEnvironmentVMDiskDiscard].(string) discard := block[mkResourceVirtualEnvironmentVMDiskDiscard].(string)
speedBlock, err := getSchemaBlock( speedBlock, err := structure.GetSchemaBlock(
resource, resource,
d, d,
[]string{mkResourceVirtualEnvironmentVMDisk, mkResourceVirtualEnvironmentVMDiskSpeed}, []string{mkResourceVirtualEnvironmentVMDisk, mkResourceVirtualEnvironmentVMDiskSpeed},
@ -3016,7 +3018,7 @@ func vmGetSerialDeviceValidator() schema.SchemaValidateDiagFunc {
func vmGetVGADeviceObject(d *schema.ResourceData) (*vms.CustomVGADevice, error) { func vmGetVGADeviceObject(d *schema.ResourceData) (*vms.CustomVGADevice, error) {
resource := VM() resource := VM()
vgaBlock, err := getSchemaBlock( vgaBlock, err := structure.GetSchemaBlock(
resource, resource,
d, d,
[]string{mkResourceVirtualEnvironmentVMVGA}, []string{mkResourceVirtualEnvironmentVMVGA},
@ -4126,7 +4128,7 @@ func vmReadNetworkValues(
if started { if started {
if vmConfig.Agent != nil && vmConfig.Agent.Enabled != nil && *vmConfig.Agent.Enabled { if vmConfig.Agent != nil && vmConfig.Agent.Enabled != nil && *vmConfig.Agent.Enabled {
resource := VM() resource := VM()
agentBlock, err := getSchemaBlock( agentBlock, err := structure.GetSchemaBlock(
resource, resource,
d, d,
[]string{mkResourceVirtualEnvironmentVMAgent}, []string{mkResourceVirtualEnvironmentVMAgent},
@ -4453,7 +4455,7 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
// Prepare the new agent configuration. // Prepare the new agent configuration.
if d.HasChange(mkResourceVirtualEnvironmentVMAgent) { if d.HasChange(mkResourceVirtualEnvironmentVMAgent) {
agentBlock, err := getSchemaBlock( agentBlock, err := structure.GetSchemaBlock(
resource, resource,
d, d,
[]string{mkResourceVirtualEnvironmentVMAgent}, []string{mkResourceVirtualEnvironmentVMAgent},
@ -4513,7 +4515,7 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
// Prepare the new CD-ROM configuration. // Prepare the new CD-ROM configuration.
if d.HasChange(mkResourceVirtualEnvironmentVMCDROM) { if d.HasChange(mkResourceVirtualEnvironmentVMCDROM) {
cdromBlock, err := getSchemaBlock( cdromBlock, err := structure.GetSchemaBlock(
resource, resource,
d, d,
[]string{mkResourceVirtualEnvironmentVMCDROM}, []string{mkResourceVirtualEnvironmentVMCDROM},
@ -4564,7 +4566,7 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
// Prepare the new CPU configuration. // Prepare the new CPU configuration.
if d.HasChange(mkResourceVirtualEnvironmentVMCPU) { if d.HasChange(mkResourceVirtualEnvironmentVMCPU) {
cpuBlock, err := getSchemaBlock( cpuBlock, err := structure.GetSchemaBlock(
resource, resource,
d, d,
[]string{mkResourceVirtualEnvironmentVMCPU}, []string{mkResourceVirtualEnvironmentVMCPU},
@ -4726,7 +4728,7 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
// Prepare the new memory configuration. // Prepare the new memory configuration.
if d.HasChange(mkResourceVirtualEnvironmentVMMemory) { if d.HasChange(mkResourceVirtualEnvironmentVMMemory) {
memoryBlock, err := getSchemaBlock( memoryBlock, err := structure.GetSchemaBlock(
resource, resource,
d, d,
[]string{mkResourceVirtualEnvironmentVMMemory}, []string{mkResourceVirtualEnvironmentVMMemory},
@ -4775,7 +4777,7 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
// Prepare the new operating system configuration. // Prepare the new operating system configuration.
if d.HasChange(mkResourceVirtualEnvironmentVMOperatingSystem) { if d.HasChange(mkResourceVirtualEnvironmentVMOperatingSystem) {
operatingSystem, err := getSchemaBlock( operatingSystem, err := structure.GetSchemaBlock(
resource, resource,
d, d,
[]string{mkResourceVirtualEnvironmentVMOperatingSystem}, []string{mkResourceVirtualEnvironmentVMOperatingSystem},
@ -5112,3 +5114,138 @@ func vmDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
return nil return nil
} }
func diskDigitPrefix(s string) string {
for i, r := range s {
if unicode.IsDigit(r) {
return s[:i]
}
}
return s
}
func getDiskInfo(resp *vms.GetResponseData, d *schema.ResourceData) map[string]*vms.CustomStorageDevice {
currentDisk := d.Get(mkResourceVirtualEnvironmentVMDisk)
currentDiskList := currentDisk.([]interface{})
currentDiskMap := map[string]map[string]interface{}{}
for _, v := range currentDiskList {
diskMap := v.(map[string]interface{})
diskInterface := diskMap[mkResourceVirtualEnvironmentVMDiskInterface].(string)
currentDiskMap[diskInterface] = diskMap
}
storageDevices := map[string]*vms.CustomStorageDevice{}
storageDevices["ide0"] = resp.IDEDevice0
storageDevices["ide1"] = resp.IDEDevice1
storageDevices["ide2"] = resp.IDEDevice2
storageDevices["ide3"] = resp.IDEDevice3
storageDevices["sata0"] = resp.SATADevice0
storageDevices["sata1"] = resp.SATADevice1
storageDevices["sata2"] = resp.SATADevice2
storageDevices["sata3"] = resp.SATADevice3
storageDevices["sata4"] = resp.SATADevice4
storageDevices["sata5"] = resp.SATADevice5
storageDevices["scsi0"] = resp.SCSIDevice0
storageDevices["scsi1"] = resp.SCSIDevice1
storageDevices["scsi2"] = resp.SCSIDevice2
storageDevices["scsi3"] = resp.SCSIDevice3
storageDevices["scsi4"] = resp.SCSIDevice4
storageDevices["scsi5"] = resp.SCSIDevice5
storageDevices["scsi6"] = resp.SCSIDevice6
storageDevices["scsi7"] = resp.SCSIDevice7
storageDevices["scsi8"] = resp.SCSIDevice8
storageDevices["scsi9"] = resp.SCSIDevice9
storageDevices["scsi10"] = resp.SCSIDevice10
storageDevices["scsi11"] = resp.SCSIDevice11
storageDevices["scsi12"] = resp.SCSIDevice12
storageDevices["scsi13"] = resp.SCSIDevice13
storageDevices["virtio0"] = resp.VirtualIODevice0
storageDevices["virtio1"] = resp.VirtualIODevice1
storageDevices["virtio2"] = resp.VirtualIODevice2
storageDevices["virtio3"] = resp.VirtualIODevice3
storageDevices["virtio4"] = resp.VirtualIODevice4
storageDevices["virtio5"] = resp.VirtualIODevice5
storageDevices["virtio6"] = resp.VirtualIODevice6
storageDevices["virtio7"] = resp.VirtualIODevice7
storageDevices["virtio8"] = resp.VirtualIODevice8
storageDevices["virtio9"] = resp.VirtualIODevice9
storageDevices["virtio10"] = resp.VirtualIODevice10
storageDevices["virtio11"] = resp.VirtualIODevice11
storageDevices["virtio12"] = resp.VirtualIODevice12
storageDevices["virtio13"] = resp.VirtualIODevice13
storageDevices["virtio14"] = resp.VirtualIODevice14
storageDevices["virtio15"] = resp.VirtualIODevice15
for k, v := range storageDevices {
if v != nil {
if currentDiskMap[k] != nil {
if currentDiskMap[k][mkResourceVirtualEnvironmentVMDiskFileID] != nil {
fileID := currentDiskMap[k][mkResourceVirtualEnvironmentVMDiskFileID].(string)
v.FileID = &fileID
}
}
// defensive copy of the loop variable
iface := k
v.Interface = &iface
}
}
return storageDevices
}
// getDiskDatastores returns a list of the used datastores in a VM.
func getDiskDatastores(vm *vms.GetResponseData, d *schema.ResourceData) []string {
storageDevices := getDiskInfo(vm, d)
datastoresSet := map[string]int{}
for _, diskInfo := range storageDevices {
// Ignore empty storage devices and storage devices (like ide) which may not have any media mounted
if diskInfo == nil || diskInfo.FileVolume == "none" {
continue
}
fileIDParts := strings.Split(diskInfo.FileVolume, ":")
datastoresSet[fileIDParts[0]] = 1
}
if vm.EFIDisk != nil {
fileIDParts := strings.Split(vm.EFIDisk.FileVolume, ":")
datastoresSet[fileIDParts[0]] = 1
}
datastores := []string{}
for datastore := range datastoresSet {
datastores = append(datastores, datastore)
}
return datastores
}
func getPCIInfo(resp *vms.GetResponseData, _ *schema.ResourceData) map[string]*vms.CustomPCIDevice {
pciDevices := map[string]*vms.CustomPCIDevice{}
pciDevices["hostpci0"] = resp.PCIDevice0
pciDevices["hostpci1"] = resp.PCIDevice1
pciDevices["hostpci2"] = resp.PCIDevice2
pciDevices["hostpci3"] = resp.PCIDevice3
return pciDevices
}
func parseImportIDWithNodeName(id string) (string, string, error) {
nodeName, id, found := strings.Cut(id, "/")
if !found {
return "", "", fmt.Errorf("unexpected format of ID (%s), expected node/id", id)
}
return nodeName, id, nil
}

View File

@ -10,6 +10,7 @@ import (
"testing" "testing"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/stretchr/testify/require"
"github.com/bpg/terraform-provider-proxmox/proxmoxtf/test" "github.com/bpg/terraform-provider-proxmox/proxmoxtf/test"
) )
@ -437,3 +438,38 @@ func TestVMSchema(t *testing.T) {
mkResourceVirtualEnvironmentVMVGAType: schema.TypeString, mkResourceVirtualEnvironmentVMVGAType: schema.TypeString,
}) })
} }
func Test_parseImportIDWIthNodeName(t *testing.T) {
t.Parallel()
tests := []struct {
name string
value string
valid bool
expectedNodeName string
expectedID string
}{
{"empty", "", false, "", ""},
{"missing slash", "invalid", false, "", ""},
{"valid", "host/id", true, "host", "id"},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
require := require.New(t)
nodeName, id, err := parseImportIDWithNodeName(tt.value)
if !tt.valid {
require.Error(err)
return
}
require.Nil(err)
require.Equal(tt.expectedNodeName, nodeName)
require.Equal(tt.expectedID, id)
})
}
}

View File

@ -0,0 +1,122 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package structure
import (
"fmt"
"reflect"
"sort"
"strings"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
// MergeSchema merges the map[string]*schema.Schema from src into dst. Safety
// against conflicts is enforced by panicking.
func MergeSchema(dst, src map[string]*schema.Schema) {
for k, v := range src {
if _, ok := dst[k]; ok {
panic(fmt.Errorf("conflicting schema key: %s", k))
}
dst[k] = v
}
}
// GetSchemaBlock returns a map[string]interface{} of a nested resource by key(s) from a schema.ResourceData.
func GetSchemaBlock(
r *schema.Resource,
d *schema.ResourceData,
k []string,
i int,
allowDefault bool,
) (map[string]interface{}, error) {
var resourceBlock map[string]interface{}
var resourceData interface{}
var resourceSchema *schema.Schema
for ki, kv := range k {
if ki == 0 {
resourceData = d.Get(kv)
resourceSchema = r.Schema[kv]
} else {
mapValues := resourceData.([]interface{})
if len(mapValues) <= i {
return resourceBlock, fmt.Errorf("index out of bounds %d", i)
}
mapValue := mapValues[i].(map[string]interface{})
resourceData = mapValue[kv]
resourceSchema = resourceSchema.Elem.(*schema.Resource).Schema[kv]
}
}
list := resourceData.([]interface{})
if len(list) == 0 {
if allowDefault {
listDefault, err := resourceSchema.DefaultValue()
if err != nil {
return nil, fmt.Errorf("failed to get default value for %s: %w", strings.Join(k, "."), err)
}
list = listDefault.([]interface{})
}
}
if len(list) > i {
resourceBlock = list[i].(map[string]interface{})
}
return resourceBlock, nil
}
// SuppressIfListsAreEqualIgnoringOrder is a customdiff.SuppressionFunc that suppresses
// changes to a list if the old and new lists are equal, ignoring the order of the
// elements.
// It will be called for each list item, so it is not super efficient. It is
// recommended to use it only for small lists.
// Ref: https://github.com/hashicorp/terraform-plugin-sdk/issues/477
func SuppressIfListsAreEqualIgnoringOrder(key, _, _ string, d *schema.ResourceData) bool {
// the key is a path to the list item, not the list itself, e.g. "tags.0"
lastDotIndex := strings.LastIndex(key, ".")
if lastDotIndex != -1 {
key = key[:lastDotIndex]
}
oldData, newData := d.GetChange(key)
if oldData == nil || newData == nil {
return false
}
oldArray := oldData.([]interface{})
newArray := newData.([]interface{})
if len(oldArray) != len(newArray) {
return false
}
oldEvents := make([]string, len(oldArray))
newEvents := make([]string, len(newArray))
for i, oldEvt := range oldArray {
oldEvents[i] = fmt.Sprint(oldEvt)
}
for j, newEvt := range newArray {
newEvents[j] = fmt.Sprint(newEvt)
}
sort.Strings(oldEvents)
sort.Strings(newEvents)
return reflect.DeepEqual(oldEvents, newEvents)
}

View File

@ -1,70 +0,0 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package structure
import (
"testing"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// AssertComputedAttributes asserts that the given keys are present in the schema and are computed.
func AssertComputedAttributes(t *testing.T, s map[string]*schema.Schema, keys []string) {
t.Helper()
for _, v := range keys {
require.NotNil(t, s[v], "Error in Schema: Missing definition for \"%s\"", v)
assert.True(t, s[v].Computed, "Error in Schema: Attribute \"%s\" is not computed", v)
}
}
// AssertNestedSchemaExistence asserts that the given key is present in the schema and is a nested schema.
func AssertNestedSchemaExistence(t *testing.T, s map[string]*schema.Schema, key string) *schema.Resource {
t.Helper()
sh, ok := s[key].Elem.(*schema.Resource)
if !ok {
t.Fatalf("Error in Schema: Missing nested schema for \"%s\"", key)
return nil
}
return sh
}
// AssertOptionalArguments asserts that the given keys are present in the schema and are optional.
func AssertOptionalArguments(t *testing.T, s map[string]*schema.Schema, keys []string) {
t.Helper()
for _, v := range keys {
require.NotNil(t, s[v], "Error in Schema: Missing definition for \"%s\"", v)
assert.True(t, s[v].Optional, "Error in Schema: Argument \"%s\" is not optional", v)
}
}
// AssertRequiredArguments asserts that the given keys are present in the schema and are required.
func AssertRequiredArguments(t *testing.T, s map[string]*schema.Schema, keys []string) {
t.Helper()
for _, v := range keys {
require.NotNil(t, s[v], "Error in Schema: Missing definition for \"%s\"", v)
assert.True(t, s[v].Required, "Error in Schema: Argument \"%s\" is not required", v)
}
}
// AssertValueTypes asserts that the given keys are present in the schema and are of the given type.
func AssertValueTypes(t *testing.T, s map[string]*schema.Schema, f map[string]schema.ValueType) {
t.Helper()
for fn, ft := range f {
require.NotNil(t, s[fn], "Error in Schema: Missing definition for \"%s\"", fn)
assert.Equal(t, ft, s[fn].Type, "Error in Schema: Argument or attribute \"%s\" is not of type \"%v\"", fn, ft)
}
}

View File

@ -1,25 +0,0 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package structure
import (
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
// MergeSchema merges the map[string]*schema.Schema from src into dst. Safety
// against conflicts is enforced by panicking.
func MergeSchema(dst, src map[string]*schema.Schema) {
for k, v := range src {
if _, ok := dst[k]; ok {
panic(fmt.Errorf("conflicting schema key: %s", k))
}
dst[k] = v
}
}