From 3b0d1a8977520f89d2afa821bc0eb2af1773fb8e Mon Sep 17 00:00:00 2001 From: Antoine Toussaint <312536060+antoinetoussaint-byte@users.noreply.github.com> Date: Wed, 7 Oct 2026 20:47:44 -0400 Subject: [PATCH] fix(deployment): quote ConfigMap values with %q, as the go agents already do MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same defect as codefly-dev/service-python-fastapi#79, found by comparing all four agents' templates. A rendered ConfigMap value is an arbitrary string — a JSON document, a comma-joined list, a sentence. This template wrapped it in hand-written quotes, so the first " inside a value closed the string early and the rest became YAML. The render then refuses its own output configmap.yaml: yaml: line 6: did not find expected key and deletes the tree it just wrote, leaving nothing to read. service-go and service-go-grpc already render `{{ printf "%q" $value }}`. This agent and service-python-fastapi did not, which is why a frontend whose configuration carries a JSON value cannot render. The test executes the template directly, because the fault is its quoting and nothing above it. It fails on the old template with the production symptom and passes on the new one; it asserts the document parses AND that every value survives byte for byte. Co-Authored-By: Claude Opus 5 (1M context) --- configmap_quoting_test.go | 55 +++++++++++++++++++ .../overlays/environment/configmap.yaml.tmpl | 2 +- 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 configmap_quoting_test.go diff --git a/configmap_quoting_test.go b/configmap_quoting_test.go new file mode 100644 index 0000000..665b3e5 --- /dev/null +++ b/configmap_quoting_test.go @@ -0,0 +1,55 @@ +package main + +import ( + "strings" + "testing" + "text/template" + + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" +) + +// A ConfigMap value is an arbitrary string: a JSON document, a comma-joined +// list, a sentence. This template wrapped it in hand-written quotes, so the +// first `"` inside a value closed the string early and the remainder became +// YAML — a render that refuses its own output with +// +// configmap.yaml: yaml: line 6: did not find expected key +// +// and then deletes the tree it wrote, leaving nothing to read. service-go and +// service-go-grpc already rendered `printf "%q"`; this agent and +// service-python-fastapi did not (codefly-dev/service-python-fastapi#79). +// +// The template is executed directly rather than through the agent's deployment +// path, because the fault is the template's quoting and nothing above it. +func TestConfigMapQuotesValuesThatContainYAMLSyntax(t *testing.T) { + hostile := map[string]string{ + "MODULE_PRINCIPALS": `{"documents":{"scopes":["read","write"]}}`, + "COLON_AND_SPACE": "key: value", + "TRAILING_BACKSLASH": `C:\path\`, + "NEWLINE": "first\nsecond", + "QUOTED_WORD": `say "hello"`, + "HASH": "value # not a comment", + "EMPTY": "", + } + raw, err := deploymentFS.ReadFile("templates/deployment/kustomize/overlays/environment/configmap.yaml.tmpl") + require.NoError(t, err) + parsed, err := template.New("configmap").Parse(string(raw)) + require.NoError(t, err) + + var out strings.Builder + require.NoError(t, parsed.Execute(&out, map[string]any{ + "Name": "frontend", + "Namespace": "platform-obin-saas", + "ConfigMap": hostile, + })) + + var document struct { + Data map[string]string `yaml:"data"` + } + require.NoError(t, yaml.Unmarshal([]byte(out.String()), &document), + "the rendered configmap is not parseable YAML:\n%s", out.String()) + for key, want := range hostile { + require.Equal(t, want, document.Data[key], "value for %s did not survive rendering", key) + } +} diff --git a/templates/deployment/kustomize/overlays/environment/configmap.yaml.tmpl b/templates/deployment/kustomize/overlays/environment/configmap.yaml.tmpl index 61ec673..b212790 100644 --- a/templates/deployment/kustomize/overlays/environment/configmap.yaml.tmpl +++ b/templates/deployment/kustomize/overlays/environment/configmap.yaml.tmpl @@ -5,5 +5,5 @@ metadata: namespace: {{ .Namespace }} data: {{- range $key, $value := .ConfigMap }} - {{ $key }}: "{{ $value }}" + {{ $key }}: {{ printf "%q" $value }} {{- end }}