diff --git a/fwprovider/provider.go b/fwprovider/provider.go index ed7e2679..cc4f78dd 100644 --- a/fwprovider/provider.go +++ b/fwprovider/provider.go @@ -257,7 +257,7 @@ func (p *proxmoxProvider) Configure( req provider.ConfigureRequest, resp *provider.ConfigureResponse, ) { - tflog.Info(ctx, "Configuring the Proxmox provider...") + tflog.Info(ctx, "Configuring the Framework Proxmox provider...") // Retrieve provider data from configuration var cfg proxmoxProviderModel diff --git a/fwprovider/test/resource_vm_test.go b/fwprovider/test/resource_vm_test.go index ed99b0d5..f71627d8 100644 --- a/fwprovider/test/resource_vm_test.go +++ b/fwprovider/test/resource_vm_test.go @@ -1,3 +1,9 @@ +/* + * 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/. + */ + //go:build acceptance || all /* @@ -9,10 +15,13 @@ package test import ( + "math/rand" "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + + "github.com/bpg/terraform-provider-proxmox/utils" ) func TestAccResourceVM(t *testing.T) { @@ -142,6 +151,30 @@ func TestAccResourceVM(t *testing.T) { ), }}, }, + { + "set cpu.architecture as non root is not supported", []resource.TestStep{{ + Config: te.RenderConfig(` + resource "proxmox_virtual_environment_vm" "test_cpu_arch" { + node_name = "{{.NodeName}}" + started = false + cpu { + architecture = "x86_64" + } + }`, WithAPIToken()), + ExpectError: regexp.MustCompile(`the CPU architecture can only be set by the root account`), + }, + { + Config: te.RenderConfig(` + resource "proxmox_virtual_environment_vm" "template" { + node_name = "{{.NodeName}}" + started = false + cpu { + architecture = "x86_64" + } + }`, WithRootUser()), + Destroy: false, + }}, + }, { "update memory block", []resource.TestStep{{ Config: te.RenderConfig(` @@ -518,3 +551,49 @@ func TestAccResourceVMNetwork(t *testing.T) { }) } } + +func TestAccResourceVMClone(t *testing.T) { + if utils.GetAnyStringEnv("TF_ACC") == "" { + t.Skip("Acceptance tests are disabled") + } + + te := InitEnvironment(t) + te.AddTemplateVars(map[string]interface{}{ + "TemplateVMID": 100000 + rand.Intn(99999), + }) + + tests := []struct { + name string + step []resource.TestStep + }{ + {"clone cpu.architecture as root", []resource.TestStep{{ + Config: te.RenderConfig(` + resource "proxmox_virtual_environment_vm" "template" { + node_name = "{{.NodeName}}" + vm_id = {{.TemplateVMID}} + started = false + cpu { + architecture = "x86_64" + } + } + resource "proxmox_virtual_environment_vm" "clone" { + node_name = "{{.NodeName}}" + started = false + clone { + vm_id = proxmox_virtual_environment_vm.template.vm_id + } + }`, WithRootUser()), + }}}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: te.AccProviders, + Steps: tt.step, + }) + }) + } +} diff --git a/fwprovider/test/test_environment.go b/fwprovider/test/test_environment.go index 72564044..8de9de3a 100644 --- a/fwprovider/test/test_environment.go +++ b/fwprovider/test/test_environment.go @@ -25,22 +25,20 @@ import ( "github.com/bpg/terraform-provider-proxmox/fwprovider" "github.com/bpg/terraform-provider-proxmox/proxmox/access" - "github.com/bpg/terraform-provider-proxmox/proxmox/cluster" - sdkV2provider "github.com/bpg/terraform-provider-proxmox/proxmoxtf/provider" - "github.com/bpg/terraform-provider-proxmox/proxmox/api" + "github.com/bpg/terraform-provider-proxmox/proxmox/cluster" "github.com/bpg/terraform-provider-proxmox/proxmox/nodes" "github.com/bpg/terraform-provider-proxmox/proxmox/nodes/storage" + sdkV2provider "github.com/bpg/terraform-provider-proxmox/proxmoxtf/provider" "github.com/bpg/terraform-provider-proxmox/utils" ) // Environment is a test environment for acceptance tests. type Environment struct { - t *testing.T - templateVars map[string]any - providerConfig string - NodeName string - DatastoreID string + t *testing.T + templateVars map[string]any + NodeName string + DatastoreID string AccProviders map[string]func() (tfprotov6.ProviderServer, error) once sync.Once @@ -49,6 +47,92 @@ type Environment struct { ContainerImagesServer string } +// RenderConfigOption is a configuration option for rendering the provider configuration. +type RenderConfigOption interface { + apply(rc *renderConfig) error +} + +type renderConfig struct { + providerConfig string +} + +// returns the ssh configuration section of the provider config. +func (r *renderConfig) ssh() string { + nodeName := utils.GetAnyStringEnv("PROXMOX_VE_ACC_NODE_NAME") + if nodeName == "" { + nodeName = "pve" + } + + nodeAddress := utils.GetAnyStringEnv("PROXMOX_VE_ACC_NODE_SSH_ADDRESS") + if nodeAddress == "" { + endpoint := utils.GetAnyStringEnv("PROXMOX_VE_ENDPOINT") + + u, err := url.Parse(endpoint) + if err != nil { + panic(err) + } + + nodeAddress = u.Hostname() + } + + nodePort := utils.GetAnyStringEnv("PROXMOX_VE_ACC_NODE_SSH_PORT") + if nodePort == "" { + nodePort = "22" + } + + // one indent level + return fmt.Sprintf(` + ssh { + node { + name = "%s" + address = "%s" + port = %s + } + }`, nodeName, nodeAddress, nodePort) +} + +// WithRootUser returns a configuration option that sets the root user in the provider configuration. +func WithRootUser() RenderConfigOption { + return &rootUserConfigOption{} +} + +type rootUserConfigOption struct{} + +func (o *rootUserConfigOption) apply(rc *renderConfig) error { + if utils.GetAnyStringEnv("PROXMOX_VE_USERNAME") == "" || utils.GetAnyStringEnv("PROXMOX_VE_PASSWORD") == "" { + return fmt.Errorf("PROXMOX_VE_USERNAME and PROXMOX_VE_PASSWORD must be set") + } + + rootUser := fmt.Sprintf("\tusername = \"%s\"\n\tpassword = \"%s\"\n\tapi_token = \"\"", + utils.GetAnyStringEnv("PROXMOX_VE_USERNAME"), + utils.GetAnyStringEnv("PROXMOX_VE_PASSWORD"), + ) + + rc.providerConfig = fmt.Sprintf("provider \"proxmox\" {\n%s\n%s\n}", rootUser, rc.ssh()) + + return nil +} + +// WithAPIToken returns a configuration option that sets the API token in the provider configuration. +func WithAPIToken() RenderConfigOption { + return &apiTokenConfigOption{} +} + +type apiTokenConfigOption struct{} + +func (o *apiTokenConfigOption) apply(rc *renderConfig) error { + if utils.GetAnyStringEnv("PROXMOX_VE_API_TOKEN") == "" { + return fmt.Errorf("PROXMOX_VE_API_TOKEN must be set") + } + + apiToken := fmt.Sprintf("\tapi_token = \"%s\"\n\tusername = \"\"\n\tpassword = \"\"", + utils.GetAnyStringEnv("PROXMOX_VE_API_TOKEN")) + + rc.providerConfig = fmt.Sprintf("provider \"proxmox\" {\n%s\n%s\n}", apiToken, rc.ssh()) + + return nil +} + // InitEnvironment initializes a new test environment for acceptance tests. func InitEnvironment(t *testing.T) *Environment { t.Helper() @@ -58,33 +142,6 @@ func InitEnvironment(t *testing.T) *Environment { nodeName = "pve" } - nodeAddress := utils.GetAnyStringEnv("PROXMOX_VE_ACC_NODE_SSH_ADDRESS") - if nodeAddress == "" { - endpoint := utils.GetAnyStringEnv("PROXMOX_VE_ENDPOINT") - u, err := url.Parse(endpoint) - require.NoError(t, err) - - nodeAddress = u.Hostname() - } - - nodePort := utils.GetAnyStringEnv("PROXMOX_VE_ACC_NODE_SSH_PORT") - if nodePort == "" { - nodePort = "22" - } - - pc := fmt.Sprintf(` -provider "proxmox" { - ssh { - node { - name = "%s" - address = "%s" - port = %s - } - } - //random_vm_ids = true -} -`, nodeName, nodeAddress, nodePort) - const datastoreID = "local" cloudImagesServer := utils.GetAnyStringEnv("PROXMOX_VE_ACC_CLOUD_IMAGES_SERVER") @@ -100,18 +157,17 @@ provider "proxmox" { return &Environment{ t: t, templateVars: map[string]any{ - "ProviderConfig": pc, "NodeName": nodeName, "DatastoreID": datastoreID, "CloudImagesServer": cloudImagesServer, "ContainerImagesServer": containerImagesServer, }, - providerConfig: pc, NodeName: nodeName, DatastoreID: datastoreID, - AccProviders: muxProviders(t), CloudImagesServer: cloudImagesServer, ContainerImagesServer: containerImagesServer, + + AccProviders: muxProviders(t), } } @@ -125,18 +181,32 @@ func (e *Environment) AddTemplateVars(vars map[string]any) { } // RenderConfig renders the given configuration with for the current test environment using template engine. -func (e *Environment) RenderConfig(cfg string) string { - tmpl, err := template.New("config").Parse("{{.ProviderConfig}}" + cfg) +func (e *Environment) RenderConfig(cfg string, opt ...RenderConfigOption) string { + if len(opt) == 0 { + opt = append(opt, WithAPIToken()) + } + + rc := &renderConfig{} + for _, o := range opt { + err := o.apply(rc) + require.NoError(e.t, err, "configuration error") + } + + tmpl, err := template.New("config").Parse(cfg) require.NoError(e.t, err) var buf bytes.Buffer err = tmpl.Execute(&buf, e.templateVars) require.NoError(e.t, err) - return buf.String() + return rc.providerConfig + "\n" + buf.String() } // Client returns a new API client for the test environment. +// The client will be using the credentials from the environment variables, in precedence order: +// 1. API token +// 2. Ticket +// 3. User credentials. func (e *Environment) Client() api.Client { if e.c == nil { e.once.Do( @@ -188,43 +258,29 @@ func (e *Environment) ClusterClient() *cluster.Client { return &cluster.Client{Client: e.Client()} } -// testAccMuxProviders returns a map of mux servers for the acceptance tests. +// muxProviders returns a map of mux servers for the acceptance tests. func muxProviders(t *testing.T) map[string]func() (tfprotov6.ProviderServer, error) { t.Helper() - ctx := context.Background() + // Init mux servers + return map[string]func() (tfprotov6.ProviderServer, error){ + "proxmox": func() (tfprotov6.ProviderServer, error) { + return tf6muxserver.NewMuxServer(context.Background(), + providerserver.NewProtocol6(fwprovider.New("test")()), + func() tfprotov6.ProviderServer { + sdkV2Provider, err := tf5to6server.UpgradeServer( + context.Background(), + func() tfprotov5.ProviderServer { + return schema.NewGRPCProviderServer( + sdkV2provider.ProxmoxVirtualEnvironment(), + ) + }, + ) + require.NoError(t, err) - // Init sdkV2 provider - sdkV2Provider, err := tf5to6server.UpgradeServer( - ctx, - func() tfprotov5.ProviderServer { - return schema.NewGRPCProviderServer( - sdkV2provider.ProxmoxVirtualEnvironment(), + return sdkV2Provider + }, ) }, - ) - require.NoError(t, err) - - // Init framework provider - frameworkProvider := fwprovider.New("test")() - - providers := []func() tfprotov6.ProviderServer{ - providerserver.NewProtocol6(frameworkProvider), - func() tfprotov6.ProviderServer { - return sdkV2Provider - }, } - - // Init mux servers - muxServers := map[string]func() (tfprotov6.ProviderServer, error){ - "proxmox": func() (tfprotov6.ProviderServer, error) { - muxServer, e := tf6muxserver.NewMuxServer(ctx, providers...) - if e != nil { - return nil, fmt.Errorf("failed to create mux server: %w", e) - } - return muxServer, nil - }, - } - - return muxServers } diff --git a/proxmox/api/authenticator.go b/proxmox/api/authenticator.go index 3586bc3a..73a96801 100644 --- a/proxmox/api/authenticator.go +++ b/proxmox/api/authenticator.go @@ -16,11 +16,11 @@ import ( // is configured to use the root user. type Authenticator interface { // IsRoot returns true if the authenticator is configured to use the root - IsRoot() bool + IsRoot(ctx context.Context) bool // IsRootTicket returns true if the authenticator is configured to use the root directly using a login ticket. // (root using token is weaker, cannot change VM arch) - IsRootTicket() bool + IsRootTicket(ctx context.Context) bool // AuthenticateRequest adds authentication data to a new request. AuthenticateRequest(ctx context.Context, req *http.Request) error diff --git a/proxmox/api/client.go b/proxmox/api/client.go index 10fc38e4..3a681a19 100644 --- a/proxmox/api/client.go +++ b/proxmox/api/client.go @@ -48,11 +48,11 @@ type Client interface { ExpandPath(path string) string // IsRoot returns true if the client is configured with the root user. - IsRoot() bool + IsRoot(ctx context.Context) bool // IsRootTicket returns true if the authenticator is configured to use the root directly using a login ticket. // (root using token is weaker, cannot change VM arch) - IsRootTicket() bool + IsRootTicket(ctx context.Context) bool // HTTP returns a lower-level HTTP client. HTTP() *http.Client @@ -313,12 +313,12 @@ func (c *client) ExpandPath(path string) string { return path } -func (c *client) IsRoot() bool { - return c.auth.IsRoot() +func (c *client) IsRoot(ctx context.Context) bool { + return c.auth.IsRoot(ctx) } -func (c *client) IsRootTicket() bool { - return c.auth.IsRootTicket() +func (c *client) IsRootTicket(ctx context.Context) bool { + return c.auth.IsRootTicket(ctx) } func (c *client) HTTP() *http.Client { diff --git a/proxmox/api/client_test.go b/proxmox/api/client_test.go index 9334b9d8..6015b019 100644 --- a/proxmox/api/client_test.go +++ b/proxmox/api/client_test.go @@ -1,3 +1,9 @@ +/* + * 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 api import ( @@ -29,11 +35,11 @@ func newTestClient(fn RoundTripFunc) *http.Client { type dummyAuthenticator struct{} -func (dummyAuthenticator) IsRoot() bool { +func (dummyAuthenticator) IsRoot(_ context.Context) bool { return false } -func (dummyAuthenticator) IsRootTicket() bool { +func (dummyAuthenticator) IsRootTicket(context.Context) bool { return false } diff --git a/proxmox/api/ticket_auth.go b/proxmox/api/ticket_auth.go index dcb812bf..11e76a0e 100644 --- a/proxmox/api/ticket_auth.go +++ b/proxmox/api/ticket_auth.go @@ -40,12 +40,12 @@ func NewTicketAuthenticator(creds TicketCredentials) (Authenticator, error) { }, nil } -func (t *ticketAuthenticator) IsRoot() bool { +func (t *ticketAuthenticator) IsRoot(_ context.Context) bool { return t.authData != nil && t.authData.Username == rootUsername } -func (t *ticketAuthenticator) IsRootTicket() bool { - return t.IsRoot() +func (t *ticketAuthenticator) IsRootTicket(ctx context.Context) bool { + return t.IsRoot(ctx) } // AuthenticateRequest adds authentication data to a new request. diff --git a/proxmox/api/token_auth.go b/proxmox/api/token_auth.go index f5c5c0b3..d7ae9036 100644 --- a/proxmox/api/token_auth.go +++ b/proxmox/api/token_auth.go @@ -26,11 +26,11 @@ func NewTokenAuthenticator(toc TokenCredentials) (Authenticator, error) { }, nil } -func (t *tokenAuthenticator) IsRoot() bool { +func (t *tokenAuthenticator) IsRoot(_ context.Context) bool { return t.username == rootUsername } -func (t *tokenAuthenticator) IsRootTicket() bool { +func (t *tokenAuthenticator) IsRootTicket(_ context.Context) bool { // Logged using a token, therefore not a ticket login return false } diff --git a/proxmox/api/user_auth.go b/proxmox/api/user_auth.go index 5c112c32..3bb8a5da 100644 --- a/proxmox/api/user_auth.go +++ b/proxmox/api/user_auth.go @@ -121,12 +121,22 @@ func (t *userAuthenticator) authenticate(ctx context.Context) (*AuthenticationRe return resBody.Data, nil } -func (t *userAuthenticator) IsRoot() bool { +func (t *userAuthenticator) IsRoot(ctx context.Context) bool { + if t.authData == nil { + if _, err := t.authenticate(ctx); err != nil { + tflog.Warn(ctx, "Failed to authenticate while checking root status", map[string]interface{}{ + "error": err.Error(), + }) + + return false + } + } + return t.authData != nil && t.authData.Username == rootUsername } -func (t *userAuthenticator) IsRootTicket() bool { - return t.IsRoot() +func (t *userAuthenticator) IsRootTicket(ctx context.Context) bool { + return t.IsRoot(ctx) } // AuthenticateRequest adds authentication data to a new request. diff --git a/proxmoxtf/provider/provider.go b/proxmoxtf/provider/provider.go index 18e1a5c2..fd27a203 100644 --- a/proxmoxtf/provider/provider.go +++ b/proxmoxtf/provider/provider.go @@ -34,7 +34,7 @@ func ProxmoxVirtualEnvironment() *schema.Provider { } } -func providerConfigure(_ context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { +func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { var err error var diags diag.Diagnostics @@ -47,6 +47,8 @@ func providerConfigure(_ context.Context, d *schema.ResourceData) (interface{}, var conn *api.Connection + tflog.Info(ctx, "Configuring the SDK Proxmox provider...") + // Check environment variables endpoint := utils.GetAnyStringEnv("PROXMOX_VE_ENDPOINT", "PM_VE_ENDPOINT") insecure := utils.GetAnyBoolEnv("PROXMOX_VE_INSECURE", "PM_VE_INSECURE") @@ -78,7 +80,8 @@ func providerConfigure(_ context.Context, d *schema.ResourceData) (interface{}, csrfPreventionToken = v.(string) } - if v, ok := d.GetOk(mkProviderAPIToken); ok { + //nolint:staticcheck //using GetOkExists to check if the value is set, as it can be an empty string in tests + if v, ok := d.GetOkExists(mkProviderAPIToken); ok { apiToken = v.(string) } @@ -86,11 +89,13 @@ func providerConfigure(_ context.Context, d *schema.ResourceData) (interface{}, otp = v.(string) } - if v, ok := d.GetOk(mkProviderUsername); ok { + ///nolint:staticcheck //using GetOkExists to check if the value is set, as it can be an empty string in tests + if v, ok := d.GetOkExists(mkProviderUsername); ok { username = v.(string) } - if v, ok := d.GetOk(mkProviderPassword); ok { + //nolint:staticcheck //using GetOkExists to check if the value is set, as it can be an empty string in tests + if v, ok := d.GetOkExists(mkProviderPassword); ok { password = v.(string) } diff --git a/proxmoxtf/provider/schema.go b/proxmoxtf/provider/schema.go index 1522aea1..37bf30ca 100644 --- a/proxmoxtf/provider/schema.go +++ b/proxmoxtf/provider/schema.go @@ -77,11 +77,11 @@ func createSchema() map[string]*schema.Schema { ValidateFunc: validation.StringIsNotEmpty, }, mkProviderAPIToken: { - Type: schema.TypeString, - Optional: true, - Sensitive: true, - Description: "The API token for the Proxmox VE API.", - ValidateFunc: validation.StringIsNotEmpty, + Type: schema.TypeString, + Optional: true, + Sensitive: true, + Description: "The API token for the Proxmox VE API.", + // note: we allow empty string as a valid value, as it is used to unset the token in tests }, mkProviderOTP: { Type: schema.TypeString, @@ -91,17 +91,17 @@ func createSchema() map[string]*schema.Schema { "Please use the `api_token` attribute instead.", }, mkProviderUsername: { - Type: schema.TypeString, - Optional: true, - Description: "The username for the Proxmox VE API.", - ValidateFunc: validation.StringIsNotEmpty, + Type: schema.TypeString, + Optional: true, + Description: "The username for the Proxmox VE API.", + // note: we allow empty string as a valid value, as it is used to unset the username in tests }, mkProviderPassword: { - Type: schema.TypeString, - Optional: true, - Sensitive: true, - Description: "The password for the Proxmox VE API.", - ValidateFunc: validation.StringIsNotEmpty, + Type: schema.TypeString, + Optional: true, + Sensitive: true, + Description: "The password for the Proxmox VE API.", + // note: we allow empty string as a valid value, as it is used to unset the password in tests }, mkProviderSSH: { Type: schema.TypeList, diff --git a/proxmoxtf/resource/vm/vm.go b/proxmoxtf/resource/vm/vm.go index 3432d04a..372f207e 100644 --- a/proxmoxtf/resource/vm/vm.go +++ b/proxmoxtf/resource/vm/vm.go @@ -25,6 +25,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/bpg/terraform-provider-proxmox/proxmox" "github.com/bpg/terraform-provider-proxmox/proxmox/api" "github.com/bpg/terraform-provider-proxmox/proxmox/cluster" "github.com/bpg/terraform-provider-proxmox/proxmox/helpers/ptr" @@ -1531,6 +1532,10 @@ func VM() *schema.Resource { customdiff.ForceNewIf( mkVMID, func(_ context.Context, d *schema.ResourceDiff, _ interface{}) bool { + if !d.HasChange(mkVMID) { + return false + } + newValue := d.Get(mkVMID) // 'vm_id' is ForceNew, except when changing 'vm_id' to existing correct id @@ -1541,6 +1546,10 @@ func VM() *schema.Resource { customdiff.ForceNewIf( mkNodeName, func(_ context.Context, d *schema.ResourceDiff, _ interface{}) bool { + if !d.HasChange(mkNodeName) { + return false + } + return !d.Get(mkMigrate).(bool) }, ), @@ -1945,9 +1954,8 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d cpuFlagsConverted[fi] = flag.(string) } - // Only the root account is allowed to change the CPU architecture, which makes this check necessary. - if client.API().IsRootTicket() && cpuArchitecture != "" { - updateBody.CPUArchitecture = &cpuArchitecture + if err := setCPUArchitecture(ctx, cpuArchitecture, client, updateBody); err != nil { + return diag.FromErr(err) } updateBody.CPUCores = ptr.Ptr(int64(cpuCores)) @@ -2295,6 +2303,25 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d return vmCreateStart(ctx, d, m) } +func setCPUArchitecture( + ctx context.Context, + cpuArchitecture string, + client proxmox.Client, + updateBody *vms.UpdateRequestBody, +) error { + // Only the root account is allowed to change the CPU architecture. + if cpuArchitecture != "" { + if client.API().IsRootTicket(ctx) { + updateBody.CPUArchitecture = &cpuArchitecture + } else { + return errors.New("the `cpu.architecture` can only be set by the root account. " + + "Please switch to the root account or remove the `cpu.architecture` from the VM configuration") + } + } + + return nil +} + func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { createTimeoutSec := d.Get(mkTimeoutCreate).(int) @@ -2672,9 +2699,8 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{}) CustomStorageDevices: diskDeviceObjects, } - // Only the root account is allowed to change the CPU architecture, which makes this check necessary. - if client.API().IsRootTicket() && cpuArchitecture != "" { - createBody.CPUArchitecture = &cpuArchitecture + if err = setCPUArchitecture(ctx, cpuArchitecture, client, createBody); err != nil { + return diag.FromErr(err) } if cpuHotplugged > 0 { @@ -3570,12 +3596,7 @@ func vmReadCustom( cpu[mkCPUArchitecture] = *vmConfig.CPUArchitecture } else { // Default value of "arch" is "" according to the API documentation. - // However, assume the provider's default value as a workaround when the root account is not being used. - if !client.API().IsRootTicket() { - cpu[mkCPUArchitecture] = dvCPUArchitecture - } else { - cpu[mkCPUArchitecture] = "" - } + cpu[mkCPUArchitecture] = "" } if vmConfig.CPUCores != nil { @@ -4987,9 +5008,8 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D cpuUnits := cpuBlock[mkCPUUnits].(int) cpuAffinity := cpuBlock[mkCPUAffinity].(string) - // Only the root account is allowed to change the CPU architecture, which makes this check necessary. - if client.API().IsRootTicket() && cpuArchitecture != "" { - updateBody.CPUArchitecture = &cpuArchitecture + if err = setCPUArchitecture(ctx, cpuArchitecture, client, updateBody); err != nil { + return diag.FromErr(err) } updateBody.CPUCores = ptr.Ptr(int64(cpuCores)) diff --git a/testacc b/testacc index cd66963b..b022b53b 100755 --- a/testacc +++ b/testacc @@ -7,4 +7,4 @@ # # shellcheck disable=SC2046 -TF_ACC=1 env $(xargs < testacc.env) go test -v -count 1 --tags=acceptance -timeout 360s -run "$1" github.com/bpg/terraform-provider-proxmox/fwprovider/... $2 +TF_ACC=1 env $(xargs < testacc.env) go test -count 1 --tags=acceptance -timeout 360s -run "$1" github.com/bpg/terraform-provider-proxmox/fwprovider/... $2