Fix Proxmox tag color parsing (#1348)

This commit is contained in:
rcourtman 2026-03-25 10:40:31 +00:00
parent 40249947ed
commit 1885bd02c0
3 changed files with 83 additions and 3 deletions

View file

@ -48,6 +48,18 @@ describe('tagColors', () => {
expect(colorA.bg).not.toBe(colorB.bg);
});
it('prefers proxmox-supplied colors over fallback palettes', () => {
const color = getTagColorWithSpecial('Production', false, {
production: '#112233',
});
expect(color).toEqual({
bg: '#112233',
text: '#ffffff',
border: '#112233',
});
});
it('generates dark mode hash-based colors', () => {
const color = getTagColorWithSpecial('custom', true);

View file

@ -1821,15 +1821,42 @@ func ParseTagColorMap(tagStyle string) map[string]string {
continue
}
for _, pair := range strings.Split(strings.TrimPrefix(part, "color-map="), ";") {
kv := strings.SplitN(pair, ":", 2)
if len(kv) == 2 && len(kv[1]) == 6 {
colors[strings.ToLower(strings.TrimSpace(kv[0]))] = "#" + kv[1]
fields := strings.Split(pair, ":")
if len(fields) < 2 {
continue
}
tag := strings.ToLower(strings.TrimSpace(fields[0]))
hex := strings.TrimSpace(fields[1])
hex = strings.TrimPrefix(hex, "#")
if tag == "" || !isHexColorToken(hex) {
continue
}
colors[tag] = "#" + strings.ToLower(hex)
}
}
return colors
}
func isHexColorToken(value string) bool {
switch len(value) {
case 3, 6, 8:
default:
return false
}
for _, r := range value {
switch {
case r >= '0' && r <= '9':
case r >= 'a' && r <= 'f':
case r >= 'A' && r <= 'F':
default:
return false
}
}
return true
}
// ZFSPoolStatus represents the status of a ZFS pool (list endpoint)
type ZFSPoolStatus struct {
Name string `json:"name"`

View file

@ -7,6 +7,7 @@ import (
"math"
"net/http"
"net/http/httptest"
"reflect"
"strings"
"testing"
"time"
@ -146,6 +147,46 @@ func TestDiskUnmarshalRPM(t *testing.T) {
}
}
func TestParseTagColorMap(t *testing.T) {
tests := []struct {
name string
tagStyle string
expected map[string]string
}{
{
name: "parses documented proxmox background and text color format",
tagStyle: "color-map=Production:000000:FFFFFF;staging:ffaa00:101010,ordering=config",
expected: map[string]string{
"production": "#000000",
"staging": "#ffaa00",
},
},
{
name: "parses legacy single-color entries with leading hash",
tagStyle: "ordering=config,color-map=backup:#ABCDEF;ops:123456",
expected: map[string]string{
"backup": "#abcdef",
"ops": "#123456",
},
},
{
name: "ignores invalid color tokens",
tagStyle: "color-map=good:00ff00;bad:zzzzzz;also-bad:12345",
expected: map[string]string{
"good": "#00ff00",
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
if got := ParseTagColorMap(tc.tagStyle); !reflect.DeepEqual(got, tc.expected) {
t.Fatalf("ParseTagColorMap() = %#v, want %#v", got, tc.expected)
}
})
}
}
func TestDiskUnmarshalJSON_InvalidJSON(t *testing.T) {
var disk Disk
err := json.Unmarshal([]byte(`{invalid json`), &disk)