Skip to content

Commit df7e56d

Browse files
committed
Reduce config polling and align tag separators (#76)
1 parent f632582 commit df7e56d

5 files changed

Lines changed: 34 additions & 14 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3232
- **Group Mode Source Profile Preservation**: Fixed VMs losing their cluster association after operations in group mode, which caused "source profile not set" errors. The SourceProfile field is now preserved when refreshing individual VM data.
3333
- **Group Mode Initial Loading Feedback**: Added header loading message "Loading guest agent data" during initial startup in group mode. Previously, profile names would appear in the UI without warning after a silent enrichment process.
3434
- **CI Cache Noise**: Removed redundant Go module cache restore in CI to prevent tar "File exists" errors during lint/test/build jobs.
35+
- **Group Mode Config Polling**: Avoided cross-profile cluster resource polling when only tags change, reducing delays after saving guest configuration.
3536

3637
## [1.0.16] - 2026-01-01
3738

internal/ui/components/vm_config.go

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,11 @@ func NewVMConfigPage(app *App, vm *api.VM, config *api.VMConfig, saveFn func(*ap
195195
expectedName = page.config.Hostname
196196
}
197197

198+
nameChanged := expectedName != "" && expectedName != vm.Name
199+
tagsChanged := page.config.TagsExplicit && page.config.Tags != vm.Tags
200+
198201
// Use the dedicated polling function
199-
app.pollForConfigChange(vm, expectedName)
202+
app.pollForConfigChange(vm, expectedName, nameChanged, page.config.Tags, tagsChanged)
200203
}()
201204
}
202205
})
@@ -428,33 +431,44 @@ func showResizeStorageModal(app *App, vm *api.VM) {
428431
// pollForConfigChange polls the Proxmox API to verify that a configuration change has propagated
429432
// to both the config endpoint and the cluster resources endpoint before refreshing the UI.
430433
// This prevents race conditions where config is updated but cluster resources still show old names.
431-
func (app *App) pollForConfigChange(vm *api.VM, expectedName string) {
434+
func (app *App) pollForConfigChange(vm *api.VM, expectedName string, nameChanged bool, expectedTags string, tagsChanged bool) {
432435
client, err := app.getClientForVM(vm)
433436
if err != nil {
434437
client = app.client
435438
}
436439

437440
// Poll every 500ms for up to 15 seconds (increased timeout for cluster resources propagation)
438441
maxAttempts := 30
442+
if !nameChanged {
443+
maxAttempts = 10
444+
}
439445
for attempt := 0; attempt < maxAttempts; attempt++ {
440446
time.Sleep(500 * time.Millisecond)
441447

442448
// First check if the config endpoint has the new name using the existing API function
443449
config, err := client.GetVMConfig(vm)
444-
configUpdated := false
450+
configUpdated := true
445451

446452
if err == nil && config != nil {
447-
if vm.Type == api.VMTypeQemu && config.Name == expectedName {
448-
configUpdated = true
449-
} else if vm.Type == api.VMTypeLXC && config.Hostname == expectedName {
450-
configUpdated = true
453+
if nameChanged {
454+
if vm.Type == api.VMTypeQemu && config.Name != expectedName {
455+
configUpdated = false
456+
} else if vm.Type == api.VMTypeLXC && config.Hostname != expectedName {
457+
configUpdated = false
458+
}
459+
}
460+
461+
if tagsChanged && config.Tags != expectedTags {
462+
configUpdated = false
451463
}
464+
} else {
465+
configUpdated = false
452466
}
453467

454-
// If config is updated, also check if cluster resources reflect the change
455-
if configUpdated {
468+
// If config is updated, also check if cluster resources reflect the change when needed
469+
if configUpdated && nameChanged {
456470
// Use the existing GetVmList function to check cluster resources
457-
vmList, err := app.client.GetVmList(context.Background())
471+
vmList, err := client.GetVmList(context.Background())
458472
if err == nil {
459473
for _, vmData := range vmList {
460474
if resType, exists := vmData["type"].(string); exists && resType == vm.Type {
@@ -473,6 +487,11 @@ func (app *App) pollForConfigChange(vm *api.VM, expectedName string) {
473487
}
474488
}
475489
}
490+
} else if configUpdated {
491+
app.QueueUpdateDraw(func() {
492+
app.manualRefresh()
493+
})
494+
return
476495
}
477496
}
478497

pkg/api/vm_config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ type VMConfig struct {
1717
Memory int64 `json:"memory,omitempty"` // in bytes
1818
Description string `json:"description,omitempty"`
1919
OnBoot *bool `json:"onboot,omitempty"`
20-
// Tags is a comma-separated list of guest tags.
20+
// Tags is a semicolon-separated list of guest tags.
2121
Tags string `json:"tags,omitempty"`
2222
// TagsExplicit controls whether tags should be included in update payloads.
2323
TagsExplicit bool `json:"-"`

pkg/api/vm_config_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func TestVMConfig_ParseAndBuild(t *testing.T) {
2626
"cpu": "host",
2727
"maxmem": 16384.0,
2828
"boot": "order=scsi0;net0",
29-
"tags": "prod,db",
29+
"tags": "prod;db",
3030
},
3131
expected: &VMConfig{
3232
Name: "test-vm",
@@ -38,7 +38,7 @@ func TestVMConfig_ParseAndBuild(t *testing.T) {
3838
CPUType: "host",
3939
MaxMem: 16384,
4040
BootOrder: "order=scsi0;net0",
41-
Tags: "prod,db",
41+
Tags: "prod;db",
4242
TagsExplicit: true,
4343
},
4444
},

pkg/api/vm_types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ type VM struct {
5656
// Administrative and cluster information
5757
HAState string `json:"hastate,omitempty"` // High availability state
5858
Lock string `json:"lock,omitempty"` // Lock status if VM is locked
59-
Tags string `json:"tags,omitempty"` // Comma-separated tags
59+
Tags string `json:"tags,omitempty"` // Semicolon-separated tags
6060
Template bool `json:"template,omitempty"` // Whether this is a template
6161
Pool string `json:"pool,omitempty"` // Resource pool assignment
6262

0 commit comments

Comments
 (0)