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.
197 lines
7.6 KiB
Go
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("{}"))
|
|
}
|