From fa8ffedd746ab9d7ca3d423f61163c80d64930b4 Mon Sep 17 00:00:00 2001 From: Piotr Kazmierczak <470696+pkazmierczak@users.noreply.github.com> Date: Fri, 12 Jul 2024 08:04:27 +0200 Subject: [PATCH] api: handle newlines in JobSubmission vars correctly (#23560) Fixes a bug where variable values in job submissions that contained newlines weren't encoded correctly, and thus jobs that contained them couldn't be resumed once stopped via the UI. Internal ref: https://hashicorp.atlassian.net/browse/NET-9966 --- .changelog/23560.txt | 3 +++ api/jobs.go | 10 ++++++++++ api/jobs_test.go | 9 +++++++++ command/agent/job_endpoint.go | 2 ++ 4 files changed, 24 insertions(+) create mode 100644 .changelog/23560.txt diff --git a/.changelog/23560.txt b/.changelog/23560.txt new file mode 100644 index 000000000..e6c672b34 --- /dev/null +++ b/.changelog/23560.txt @@ -0,0 +1,3 @@ +```release-note:bug +api: Fixed bug where newlines in JobSubmission vars weren't encoded correctly +``` diff --git a/api/jobs.go b/api/jobs.go index 02c6b226d..a0788f517 100644 --- a/api/jobs.go +++ b/api/jobs.go @@ -12,6 +12,7 @@ import ( "net/url" "sort" "strconv" + "strings" "time" "github.com/hashicorp/cronexpr" @@ -997,6 +998,15 @@ func (js *JobSubmission) Canonicalize() { if len(js.VariableFlags) == 0 { js.VariableFlags = nil } + + // if there are multiline variables, make sure we escape the newline + // characters to preserve them. This way, when the job gets stopped and + // restarted in the UI, variable values will be parsed correctly. + for k, v := range js.VariableFlags { + if strings.Contains(v, "\n") { + js.VariableFlags[k] = strings.ReplaceAll(v, "\n", "\\n") + } + } } func (js *JobSubmission) Copy() *JobSubmission { diff --git a/api/jobs_test.go b/api/jobs_test.go index 2ab57479d..85873e959 100644 --- a/api/jobs_test.go +++ b/api/jobs_test.go @@ -1496,6 +1496,15 @@ func TestJobs_JobSubmission_Canonicalize(t *testing.T) { js.Canonicalize() must.Nil(t, js.VariableFlags) }) + + t.Run("multiline var values", func(t *testing.T) { + js := &JobSubmission{ + Source: "abc123", + VariableFlags: map[string]string{"test": "foo\nbar"}, + } + js.Canonicalize() + must.Eq(t, js.VariableFlags["test"], "foo\\nbar") + }) } func TestJobs_JobSubmission_Copy(t *testing.T) { diff --git a/command/agent/job_endpoint.go b/command/agent/job_endpoint.go index b12427d97..b9de05a91 100644 --- a/command/agent/job_endpoint.go +++ b/command/agent/job_endpoint.go @@ -908,6 +908,8 @@ func apiJobSubmissionToStructs(submission *api.JobSubmission) *structs.JobSubmis if submission == nil { return nil } + + submission.Canonicalize() return &structs.JobSubmission{ Source: submission.Source, Format: submission.Format,