From 11bb0696cb711dc0ee96c1863e7e1f47a8c06c54 Mon Sep 17 00:00:00 2001 From: aceforeverd Date: Tue, 10 Nov 2026 23:18:14 +0800 Subject: [PATCH] feat(site): named value presets in the site profile A site profile may list presets: named chart values, each with an optional description, that a deploy can pick one of. The console merges the picked preset into the form layer; swissd stores and serves them and composes nothing from them. The field is needed because the profile is parsed with known fields only. --- internal/site/presets_test.go | 51 +++++++++++++++++++++++++++++++++++ internal/site/profile.go | 9 +++++++ 2 files changed, 60 insertions(+) create mode 100644 internal/site/presets_test.go diff --git a/internal/site/presets_test.go b/internal/site/presets_test.go new file mode 100644 index 0000000..51113aa --- /dev/null +++ b/internal/site/presets_test.go @@ -0,0 +1,51 @@ +package site + +import ( + "reflect" + "testing" + + "gopkg.in/yaml.v3" + + "github.com/modelsphere/swiss/internal/values" +) + +func TestPresetsRoundTrip(t *testing.T) { + p, err := Parse([]byte(inputsHead+`presets: + storage-net: + description: on storage-network nodes + values: + nodeSelector: + ebof.modelsphere.io/storage-net: "true" + tolerations: + - key: ebof-test + operator: Exists + effect: NoSchedule +`), "test") + if err != nil { + t.Fatal(err) + } + got := p.Presets["storage-net"] + if got.Description != "on storage-network nodes" { + t.Errorf("description = %q", got.Description) + } + if sel, _ := values.Get(got.Values, "nodeSelector"); !reflect.DeepEqual(sel, values.Tree{"ebof.modelsphere.io/storage-net": "true"}) { + t.Errorf("nodeSelector = %#v", sel) + } + out, err := yaml.Marshal(p) + if err != nil { + t.Fatal(err) + } + again, err := Parse(out, "again") + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(again.Presets, p.Presets) { + t.Errorf("presets changed on the way back:\n%v\n%v", p.Presets, again.Presets) + } +} + +func TestPresetValuesMustBeAMap(t *testing.T) { + if _, err := Parse([]byte(inputsHead+"presets:\n x:\n values: [a]\n"), "test"); err == nil { + t.Error("a preset whose values are a list parsed") + } +} diff --git a/internal/site/profile.go b/internal/site/profile.go index 386b6a6..26c09af 100644 --- a/internal/site/profile.go +++ b/internal/site/profile.go @@ -80,6 +80,10 @@ type Profile struct { // the key -- unlike extra. Strings take pathTemplate's placeholders. Inputs map[string]any `yaml:"inputs,omitempty" json:"inputs,omitempty"` + // Presets are named values a deploy may pick one of. The console merges the + // picked one into the form layer; swissd composes nothing from them. + Presets map[string]Preset `yaml:"presets,omitempty" json:"presets,omitempty"` + // Sites are the other clusters' swissd instances, for the nav switcher. // // In the profile rather than swissd's config because it is a fact about the @@ -94,6 +98,11 @@ type Profile struct { Extra values.Tree `yaml:"extra,omitempty" json:"extra,omitempty"` } +type Preset struct { + Description string `yaml:"description,omitempty" json:"description,omitempty"` + Values values.Tree `yaml:"values,omitempty" json:"values,omitempty"` +} + // Site is one other swissd, by name and address. The URL is where a browser // goes, so it is an origin this one links to and never calls. type Site struct {