0
0
mirror of https://github.com/bpg/terraform-provider-proxmox.git synced 2025-06-29 18:21:10 +00:00

fix(vm): vga block defaults handling during create / clone (#1732)

* fix(vm): fix `vga` block defaults handling during create / clone

Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>

* fix: formatting

Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>

* fix: acc test failure due to skip

Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>

---------

Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
This commit is contained in:
Pavel Boldyrev 2025-01-30 22:11:46 -05:00 committed by GitHub
parent 4d89d3484b
commit c992dfc1f3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 154 additions and 116 deletions

5
.gitignore vendored
View File

@ -46,5 +46,6 @@ id_rsa.pub
# Binary
terraform-provider-proxmox*
# .vscode
.vscode/settings.json
# VScode / Cursor
.vscode/settings.json
.cursorrules

11
.vscode/launch.json vendored
View File

@ -1,12 +1,21 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Run Acceptance Tests",
"type": "go",
"request": "launch",
"mode": "test",
"program": "${workspaceFolder}/fwprovider/test",
"envFile": "${workspaceFolder}/testacc.env",
"args": ["-test.v", "-test.timeout", "120s"]
},
{
"name": "Debug Acceptance Tests",
"type": "go",
"request": "launch",
"mode": "test",
"program": "${workspaceFolder}/fwprovider/tests",
"program": "${workspaceFolder}/fwprovider/test",
"envFile": "${workspaceFolder}/testacc.env",
"args": ["-debug", "-test.v", "-test.timeout", "120s"]

View File

@ -127,6 +127,14 @@
]
}
],
"psi-header.lang-config": [
{
"language": "go",
"ignoreLines": [
"//go:build"
]
}
],
"makefile.configureOnOpen": false,
"go.buildFlags": ["-tags=all"],
"gopls": { "build.buildFlags": ["-tags=all"] }

View File

@ -311,7 +311,7 @@ func TestAccResourceStandardRepoValidInput(t *testing.T) {
// PUT /api2/json/nodes/{node}/apt/repositories with handle = "no-subscription" will create a new
// entry in /etc/apt/sources.list on each call :/
SkipFunc: func() (bool, error) {
return true, fmt.Errorf("skipped due to API limitation: PUT request creates new entry on each call")
return true, nil
},
Config: te.RenderConfig(`
resource "proxmox_virtual_environment_apt_standard_repository" "test" {
@ -337,8 +337,10 @@ func TestAccResourceStandardRepoValidInput(t *testing.T) {
// Test the "ImportState" implementation.
{
// PUT /api2/json/nodes/{node}/apt/repositories with handle = "no-subscription" will create a new
// entry in /etc/apt/sources.list on each call :/
SkipFunc: func() (bool, error) {
return true, fmt.Errorf("skipped due to API limitation: PUT request creates new entry on each call")
return true, nil
},
ImportState: true,
ImportStateId: fmt.Sprintf("%s,no-subscription", strings.ToLower(te.NodeName)),

View File

@ -9,7 +9,6 @@
package test
import (
"math/rand"
"regexp"
"testing"
@ -73,18 +72,16 @@ func TestAccResourceVM(t *testing.T) {
}),
),
}}},
{
"empty node_name", []resource.TestStep{{
Config: te.RenderConfig(`
{"empty node_name", []resource.TestStep{{
Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm" "test_empty_node_name" {
node_name = ""
started = false
}`),
ExpectError: regexp.MustCompile(`expected "node_name" to not be an empty string, got `),
}},
},
{
"protection", []resource.TestStep{{
ExpectError: regexp.MustCompile(`expected "node_name" to not be an empty string, got `),
}}},
{"protection", []resource.TestStep{
{
Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm" "test_vm4" {
node_name = "{{.NodeName}}"
@ -110,10 +107,10 @@ func TestAccResourceVM(t *testing.T) {
"protection": "false",
}),
),
}},
},
{
"update cpu block", []resource.TestStep{{
},
}},
{"update cpu block", []resource.TestStep{
{
Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm" "test_vm5" {
node_name = "{{.NodeName}}"
@ -143,10 +140,10 @@ func TestAccResourceVM(t *testing.T) {
"cpu.0.sockets": "1",
}),
),
}},
},
{
"set cpu.architecture as non root is not supported", []resource.TestStep{{
},
}},
{"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}}"
@ -157,8 +154,8 @@ func TestAccResourceVM(t *testing.T) {
}`, WithAPIToken()),
ExpectError: regexp.MustCompile(`can only be set by the root account`),
},
{
Config: te.RenderConfig(`
{
Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm" "template" {
node_name = "{{.NodeName}}"
started = false
@ -166,11 +163,11 @@ func TestAccResourceVM(t *testing.T) {
architecture = "x86_64"
}
}`, WithRootUser()),
Destroy: false,
}},
},
{
"update memory block", []resource.TestStep{{
Destroy: false,
},
}},
{"update memory block", []resource.TestStep{
{
Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm" "test_vm6" {
node_name = "{{.NodeName}}"
@ -200,10 +197,39 @@ func TestAccResourceVM(t *testing.T) {
"memory.0.dedicated": "1024",
}),
),
}},
},
{
"update vga block", []resource.TestStep{{
},
}},
{"create vga block", []resource.TestStep{
{
Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm" "test_vm" {
node_name = "{{.NodeName}}"
started = false
}`),
Check: resource.ComposeTestCheckFunc(
ResourceAttributes("proxmox_virtual_environment_vm.test_vm", map[string]string{
"vga.#": "0",
}),
),
}, {
Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm" "test_vm" {
node_name = "{{.NodeName}}"
started = false
vga {
type = "virtio-gl"
clipboard = "vnc"
}
}`),
Check: resource.ComposeTestCheckFunc(
ResourceAttributes("proxmox_virtual_environment_vm.test_vm", map[string]string{
"vga.#": "1",
}),
),
},
}},
{"update vga block", []resource.TestStep{
{
Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm" "test_vm" {
node_name = "{{.NodeName}}"
@ -253,10 +279,10 @@ func TestAccResourceVM(t *testing.T) {
"vga.0.clipboard": "",
}),
),
}},
},
{
"update watchdog block", []resource.TestStep{{
},
}},
{"update watchdog block", []resource.TestStep{
{
Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm" "test_vm" {
node_name = "{{.NodeName}}"
@ -309,8 +335,8 @@ func TestAccResourceVM(t *testing.T) {
"watchdog.0.action": "reset",
}),
),
}},
},
},
}},
}
for _, tt := range tests {
@ -498,8 +524,9 @@ func TestAccResourceVMNetwork(t *testing.T) {
}),
),
}}},
{"network device disconnected", []resource.TestStep{{
Config: te.RenderConfig(`
{"network device disconnected", []resource.TestStep{
{
Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm" "test_vm_network2" {
node_name = "{{.NodeName}}"
started = false
@ -508,14 +535,14 @@ func TestAccResourceVMNetwork(t *testing.T) {
bridge = "vmbr0"
}
}`),
Check: resource.ComposeTestCheckFunc(
ResourceAttributes("proxmox_virtual_environment_vm.test_vm_network2", map[string]string{
"network_device.0.bridge": "vmbr0",
"network_device.0.disconnected": "false",
}),
),
}, {
Config: te.RenderConfig(`
Check: resource.ComposeTestCheckFunc(
ResourceAttributes("proxmox_virtual_environment_vm.test_vm_network2", map[string]string{
"network_device.0.bridge": "vmbr0",
"network_device.0.disconnected": "false",
}),
),
}, {
Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm" "test_vm_network2" {
node_name = "{{.NodeName}}"
started = false
@ -525,13 +552,14 @@ func TestAccResourceVMNetwork(t *testing.T) {
disconnected = true
}
}`),
Check: resource.ComposeTestCheckFunc(
ResourceAttributes("proxmox_virtual_environment_vm.test_vm_network2", map[string]string{
"network_device.0.bridge": "vmbr0",
"network_device.0.disconnected": "true",
}),
),
}}},
Check: resource.ComposeTestCheckFunc(
ResourceAttributes("proxmox_virtual_environment_vm.test_vm_network2", map[string]string{
"network_device.0.bridge": "vmbr0",
"network_device.0.disconnected": "true",
}),
),
},
}},
}
for _, tt := range tests {
@ -552,9 +580,6 @@ func TestAccResourceVMClone(t *testing.T) {
}
te := InitEnvironment(t)
te.AddTemplateVars(map[string]interface{}{
"TemplateVMID": 100000 + rand.Intn(99999),
})
tests := []struct {
name string
@ -564,8 +589,8 @@ func TestAccResourceVMClone(t *testing.T) {
Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm" "template" {
node_name = "{{.NodeName}}"
vm_id = {{.TemplateVMID}}
started = false
template = true
cpu {
architecture = "x86_64"
}
@ -578,6 +603,25 @@ func TestAccResourceVMClone(t *testing.T) {
}
}`, WithRootUser()),
}}},
{"clone no vga block", []resource.TestStep{{
Config: te.RenderConfig(`
resource "proxmox_virtual_environment_vm" "template" {
node_name = "{{.NodeName}}"
started = false
}
resource "proxmox_virtual_environment_vm" "clone" {
node_name = "{{.NodeName}}"
started = false
clone {
vm_id = proxmox_virtual_environment_vm.template.vm_id
}
}`),
Check: resource.ComposeTestCheckFunc(
ResourceAttributes("proxmox_virtual_environment_vm.clone", map[string]string{
"vga.#": "0",
}),
),
}}},
}
for _, tt := range tests {

View File

@ -1398,15 +1398,6 @@ func VM() *schema.Resource {
Type: schema.TypeList,
Description: "The VGA configuration",
Optional: true,
DefaultFunc: func() (interface{}, error) {
return []interface{}{
map[string]interface{}{
mkVGAClipboard: dvVGAClipboard,
mkVGAMemory: dvVGAMemory,
mkVGAType: dvVGAType,
},
}, nil
},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
mkVGAClipboard: {
@ -2128,11 +2119,7 @@ func vmCreateClone(ctx context.Context, d *schema.ResourceData, m interface{}) d
}
if len(vga) > 0 {
vgaDevice, err := vmGetVGADeviceObject(d)
if err != nil {
return diag.FromErr(err)
}
vgaDevice := vmGetVGADeviceObject(d)
updateBody.VGADevice = vgaDevice
}
@ -2531,10 +2518,7 @@ func vmCreateCustom(ctx context.Context, d *schema.ResourceData, m interface{})
tabletDevice := types.CustomBool(d.Get(mkTabletDevice).(bool))
template := types.CustomBool(d.Get(mkTemplate).(bool))
vgaDevice, err := vmGetVGADeviceObject(d)
if err != nil {
return diag.FromErr(err)
}
vgaDevice := vmGetVGADeviceObject(d)
vmIDUntyped, hasVMID := d.GetOk(mkVMID)
vmID := vmIDUntyped.(int)
@ -3320,39 +3304,33 @@ func vmGetTagsString(d *schema.ResourceData) string {
return strings.Join(sanitizedTags, ";")
}
func vmGetVGADeviceObject(d *schema.ResourceData) (*vms.CustomVGADevice, error) {
resource := VM()
func vmGetVGADeviceObject(d *schema.ResourceData) *vms.CustomVGADevice {
vga := d.Get(mkVGA).([]interface{})
if len(vga) > 0 && vga[0] != nil {
vgaBlock := vga[0].(map[string]interface{})
vgaClipboard := vgaBlock[mkVGAClipboard].(string)
vgaMemory := vgaBlock[mkVGAMemory].(int)
vgaBlock, err := structure.GetSchemaBlock(
resource,
d,
[]string{mkVGA},
0,
true,
)
if err != nil {
return nil, fmt.Errorf("error getting VGA block: %w", err)
vgaType := vgaBlock[mkVGAType].(string)
vgaDevice := &vms.CustomVGADevice{}
if vgaClipboard != "" {
vgaDevice.Clipboard = &vgaClipboard
}
if vgaMemory > 0 {
vgaDevice.Memory = ptr.Ptr(int64(vgaMemory))
}
if vgaType != "" {
vgaDevice.Type = &vgaType
}
return vgaDevice
}
vgaClipboard := vgaBlock[mkVGAClipboard].(string)
vgaMemory := vgaBlock[mkVGAMemory].(int)
vgaType := vgaBlock[mkVGAType].(string)
vgaDevice := &vms.CustomVGADevice{}
if vgaClipboard != "" {
vgaDevice.Clipboard = &vgaClipboard
}
if vgaMemory > 0 {
vgaDevice.Memory = ptr.Ptr(int64(vgaMemory))
}
if vgaType != "" {
vgaDevice.Type = &vgaType
}
return vgaDevice, nil
return nil
}
func vmRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
@ -4420,9 +4398,9 @@ func vmReadCustom(
vga[mkVGAType] = *vmConfig.VGADevice.Type
}
} else {
vga[mkVGAClipboard] = ""
vga[mkVGAMemory] = 0
vga[mkVGAType] = ""
vga[mkVGAClipboard] = dvVGAClipboard
vga[mkVGAMemory] = dvVGAMemory
vga[mkVGAType] = dvVGAType
}
currentVGA := d.Get(mkVGA).([]interface{})
@ -5306,11 +5284,7 @@ func vmUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.D
// Prepare the new VGA configuration.
if d.HasChange(mkVGA) {
updateBody.VGADevice, e = vmGetVGADeviceObject(d)
if e != nil {
return diag.FromErr(e)
}
updateBody.VGADevice = vmGetVGADeviceObject(d)
rebootRequired = true
}