Files
dokku/plugins/scheduler-k3s/triggers_test.go
Jose Diaz-Gonzalez 21ed5fb738 fix: persist scheduler-k3s annotations and labels as JSON maps
Annotation and label values containing a newline previously round-tripped
as two adjacent `key: value` lines, and the second line then failed the
`SplitN(line, ": ", 2)` parse on read. Move the per-`(processType,
resourceType)` files to `PropertyMap*` storage so `\n` in values and `/`
in keys are preserved verbatim. An idempotent `TriggerInstall` migration
walks `--global` and every app, converting any legacy line-formatted file
in place by probing `PropertyMapGet` first and rewriting via
`PropertyListGet` only when the probe fails. Property names are
unchanged, so the annotations/labels report scanner and the
`reservedAnnotationPrefixes` filter keep working without modification.

Closes #8719.
2026-05-31 02:38:35 -04:00

197 lines
7.6 KiB
Go

package scheduler_k3s
import (
"encoding/json"
"os"
"path/filepath"
"testing"
"github.com/dokku/dokku/plugins/common"
. "github.com/onsi/gomega"
)
func setupChartMigrationTest(t *testing.T) {
t.Helper()
RegisterTestingT(t)
t.Setenv("PLUGIN_PATH", "/var/lib/dokku/plugins")
t.Setenv("PLUGIN_ENABLED_PATH", "/var/lib/dokku/plugins/enabled")
t.Setenv("DOKKU_LIB_ROOT", t.TempDir())
t.Setenv("DOKKU_SYSTEM_USER", "root")
t.Setenv("DOKKU_SYSTEM_GROUP", "root")
Expect(common.PropertySetup("scheduler-k3s")).To(Succeed())
}
func setupAnnotationsLabelsMigrationTest(t *testing.T, apps ...string) {
t.Helper()
setupChartMigrationTest(t)
dokkuRoot := t.TempDir()
t.Setenv("DOKKU_ROOT", dokkuRoot)
for _, appName := range apps {
Expect(os.MkdirAll(filepath.Join(dokkuRoot, appName), 0755)).To(Succeed())
}
}
// writeLegacyAnnotationLabelFile seeds a property file directly using
// PropertyListWrite, matching the pre-migration on-disk layout (one
// "key: value" entry per line).
func writeLegacyAnnotationLabelFile(t *testing.T, scope string, property string, entries map[string]string) {
t.Helper()
lines := []string{}
for k, v := range entries {
lines = append(lines, k+": "+v)
}
Expect(common.PropertyListWrite("scheduler-k3s", scope, property, lines)).To(Succeed())
}
func TestMigrateChartPropertiesToMapFormat(t *testing.T) {
setupChartMigrationTest(t)
Expect(common.PropertyWrite("scheduler-k3s", "--global", "chart.cert-manager.installCRDs", "false")).To(Succeed())
Expect(common.PropertyWrite("scheduler-k3s", "--global", "chart.cert-manager.version", "1.13.3")).To(Succeed())
Expect(common.PropertyWrite("scheduler-k3s", "--global", "chart.traefik.replicas", "2")).To(Succeed())
Expect(migrateChartPropertiesToMapFormat()).To(Succeed())
certManager, err := common.PropertyMapGet("scheduler-k3s", "--global", "chart-overrides.cert-manager")
Expect(err).NotTo(HaveOccurred())
Expect(certManager).To(Equal(map[string]string{
"installCRDs": "false",
"version": "1.13.3",
}))
traefik, err := common.PropertyMapGet("scheduler-k3s", "--global", "chart-overrides.traefik")
Expect(err).NotTo(HaveOccurred())
Expect(traefik).To(Equal(map[string]string{"replicas": "2"}))
Expect(common.PropertyExists("scheduler-k3s", "--global", "chart.cert-manager.installCRDs")).To(BeFalse())
Expect(common.PropertyExists("scheduler-k3s", "--global", "chart.cert-manager.version")).To(BeFalse())
Expect(common.PropertyExists("scheduler-k3s", "--global", "chart.traefik.replicas")).To(BeFalse())
}
func TestMigrateChartPropertiesToMapFormatIsIdempotent(t *testing.T) {
setupChartMigrationTest(t)
Expect(common.PropertyWrite("scheduler-k3s", "--global", "chart.cert-manager.version", "1.13.3")).To(Succeed())
Expect(migrateChartPropertiesToMapFormat()).To(Succeed())
Expect(migrateChartPropertiesToMapFormat()).To(Succeed())
got, err := common.PropertyMapGet("scheduler-k3s", "--global", "chart-overrides.cert-manager")
Expect(err).NotTo(HaveOccurred())
Expect(got).To(Equal(map[string]string{"version": "1.13.3"}))
}
func TestMigrateChartPropertiesToMapFormatPreservesExistingNonOverlappingKeys(t *testing.T) {
setupChartMigrationTest(t)
Expect(common.PropertyMapWrite("scheduler-k3s", "--global", "chart-overrides.cert-manager", map[string]string{
"installCRDs": "true",
"service.annotations.prometheus.io/scrape": "true",
})).To(Succeed())
Expect(common.PropertyWrite("scheduler-k3s", "--global", "chart.cert-manager.installCRDs", "false")).To(Succeed())
Expect(common.PropertyWrite("scheduler-k3s", "--global", "chart.cert-manager.version", "1.13.3")).To(Succeed())
Expect(migrateChartPropertiesToMapFormat()).To(Succeed())
got, err := common.PropertyMapGet("scheduler-k3s", "--global", "chart-overrides.cert-manager")
Expect(err).NotTo(HaveOccurred())
Expect(got).To(Equal(map[string]string{
"installCRDs": "false",
"service.annotations.prometheus.io/scrape": "true",
"version": "1.13.3",
}))
}
func TestMigrateChartPropertiesToMapFormatNoLegacyIsNoOp(t *testing.T) {
setupChartMigrationTest(t)
Expect(migrateChartPropertiesToMapFormat()).To(Succeed())
for _, chart := range HelmCharts {
Expect(common.PropertyExists("scheduler-k3s", "--global", "chart-overrides."+chart.ReleaseName)).To(BeFalse())
}
}
func TestMigrateAnnotationsLabelsToMapFormat(t *testing.T) {
setupAnnotationsLabelsMigrationTest(t, "node-js-app")
writeLegacyAnnotationLabelFile(t, "node-js-app", "web.deployment", map[string]string{
"prometheus.io/scrape": "true",
"team": "platform",
})
writeLegacyAnnotationLabelFile(t, "node-js-app", "labels.global.service", map[string]string{
"app.kubernetes.io/managed-by": "dokku",
})
writeLegacyAnnotationLabelFile(t, "--global", "global.deployment", map[string]string{
"owner": "ops",
})
Expect(migrateAnnotationsLabelsToMapFormat()).To(Succeed())
appAnnotations, err := common.PropertyMapGet("scheduler-k3s", "node-js-app", "web.deployment")
Expect(err).NotTo(HaveOccurred())
Expect(appAnnotations).To(Equal(map[string]string{
"prometheus.io/scrape": "true",
"team": "platform",
}))
appLabels, err := common.PropertyMapGet("scheduler-k3s", "node-js-app", "labels.global.service")
Expect(err).NotTo(HaveOccurred())
Expect(appLabels).To(Equal(map[string]string{
"app.kubernetes.io/managed-by": "dokku",
}))
globalAnnotations, err := common.PropertyMapGet("scheduler-k3s", "--global", "global.deployment")
Expect(err).NotTo(HaveOccurred())
Expect(globalAnnotations).To(Equal(map[string]string{"owner": "ops"}))
raw, err := os.ReadFile(filepath.Join(common.MustGetEnv("DOKKU_LIB_ROOT"), "config", "scheduler-k3s", "node-js-app", "web.deployment"))
Expect(err).NotTo(HaveOccurred())
var decoded map[string]string
Expect(json.Unmarshal(raw, &decoded)).To(Succeed())
Expect(decoded).To(Equal(appAnnotations))
}
func TestMigrateAnnotationsLabelsToMapFormatIsIdempotent(t *testing.T) {
setupAnnotationsLabelsMigrationTest(t, "node-js-app")
writeLegacyAnnotationLabelFile(t, "node-js-app", "web.deployment", map[string]string{
"prometheus.io/scrape": "true",
})
Expect(migrateAnnotationsLabelsToMapFormat()).To(Succeed())
Expect(migrateAnnotationsLabelsToMapFormat()).To(Succeed())
got, err := common.PropertyMapGet("scheduler-k3s", "node-js-app", "web.deployment")
Expect(err).NotTo(HaveOccurred())
Expect(got).To(Equal(map[string]string{"prometheus.io/scrape": "true"}))
}
func TestMigrateAnnotationsLabelsToMapFormatPreservesAlreadyMigrated(t *testing.T) {
setupAnnotationsLabelsMigrationTest(t, "node-js-app")
preserved := map[string]string{
"prometheus.io/scrape": "true",
"controller.config": "line one\nline two\nline three",
}
Expect(common.PropertyMapWrite("scheduler-k3s", "node-js-app", "web.deployment", preserved)).To(Succeed())
Expect(migrateAnnotationsLabelsToMapFormat()).To(Succeed())
got, err := common.PropertyMapGet("scheduler-k3s", "node-js-app", "web.deployment")
Expect(err).NotTo(HaveOccurred())
Expect(got).To(Equal(preserved))
}
func TestMigrateAnnotationsLabelsToMapFormatSkipsReservedPrefixes(t *testing.T) {
setupAnnotationsLabelsMigrationTest(t)
Expect(common.PropertyWrite("scheduler-k3s", "--global", "chart.cert-manager.deployment", "irrelevant")).To(Succeed())
Expect(common.PropertyWrite("scheduler-k3s", "--global", "node-profile-foo.json", "{}")).To(Succeed())
Expect(migrateAnnotationsLabelsToMapFormat()).To(Succeed())
Expect(common.PropertyGet("scheduler-k3s", "--global", "chart.cert-manager.deployment")).To(Equal("irrelevant"))
Expect(common.PropertyGet("scheduler-k3s", "--global", "node-profile-foo.json")).To(Equal("{}"))
}