From 45b7d165dd8beb0a2b16bbbcb9e4a2051713077b Mon Sep 17 00:00:00 2001 From: Wesley Ellis Date: Fri, 10 Jul 2026 10:59:00 -0400 Subject: [PATCH 1/2] Emit account_id and job metadata as Kubernetes pod labels Rather than weaving the account_id job variable into the pod name (which risks the 63-char limit and OpsLevel-side naming conventions), stamp descriptive labels on the job pod for observability: app.kubernetes.io/name: opslevel-job opslevel.com/account-id: (when the job sets it) opslevel.com/job-id: opslevel.com/mode: These enable native DataDog filtering via podLabelsAsTags with no pod name parsing. Labels are added after building the PDB label selector so they stay out of the selector, which the instance label already makes unique. Adds a getRunnerJobVariable helper (named explicitly since a similar helper is anticipated for egress proxies). Co-Authored-By: Claude Opus 4.8 --- src/pkg/k8s.go | 20 ++++++++++++++++++++ src/pkg/k8s_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/src/pkg/k8s.go b/src/pkg/k8s.go index dbbf6aa..d35c83b 100644 --- a/src/pkg/k8s.go +++ b/src/pkg/k8s.go @@ -111,6 +111,16 @@ func NewJobRunner(runnerId string, path string) *JobRunner { } } +// getRunnerJobVariable returns the value of the job variable with the given key, if present. +func getRunnerJobVariable(configs []opslevel.RunnerJobVariable, key string) string { + for _, config := range configs { + if config.Key == key { + return config.Value + } + } + return "" +} + // getPodEnv returns the env vars to inject into a container for the given // scope. Variables with no Scope set are visible to every container; variables // with a Scope are only visible to containers running in that scope. @@ -372,6 +382,16 @@ func (s *JobRunner) Run(ctx context.Context, job opslevel.RunnerJob, stdout, std Outcome: opslevel.RunnerJobOutcomeEnumFailed, } } + // Descriptive labels for observability (e.g. DataDog podLabelsAsTags), so + // running pods can be filtered by account/job/mode without parsing the pod + // name. Added after building the selector so they stay out of the PDB + // selector, which the instance label already makes unique. + labels["app.kubernetes.io/name"] = "opslevel-job" + labels["opslevel.com/job-id"] = id + labels["opslevel.com/mode"] = viper.GetString("mode") + if accountId := getRunnerJobVariable(job.Variables, "account_id"); accountId != "" { + labels["opslevel.com/account-id"] = accountId + } // TODO: manage pods based on image for re-use? cfgMap, err := s.CreateConfigMap(ctx, s.getConfigMapObject(identifier, job)) if err != nil { diff --git a/src/pkg/k8s_test.go b/src/pkg/k8s_test.go index f7322e5..a3b8722 100644 --- a/src/pkg/k8s_test.go +++ b/src/pkg/k8s_test.go @@ -193,6 +193,33 @@ func TestGetPodEnv_FiltersByScope(t *testing.T) { autopilot.Equals(t, []string{"BOTH", "MAIN_ONLY"}, mainKeys) } +func TestGetRunnerJobVariable_ReturnsMatchingValue(t *testing.T) { + // Arrange + vars := []opslevel.RunnerJobVariable{ + {Key: "FOO", Value: "bar"}, + {Key: "account_id", Value: "acct-123"}, + } + + // Act + value := getRunnerJobVariable(vars, "account_id") + + // Assert + autopilot.Equals(t, "acct-123", value) +} + +func TestGetRunnerJobVariable_ReturnsEmptyWhenMissing(t *testing.T) { + // Arrange + vars := []opslevel.RunnerJobVariable{ + {Key: "FOO", Value: "bar"}, + } + + // Act + value := getRunnerJobVariable(vars, "account_id") + + // Assert + autopilot.Equals(t, "", value) +} + func TestGetPodObject_NoInitCommands(t *testing.T) { // Arrange runner := &JobRunner{ From 2add0a45bcfd8ca8757ed4d998cbe058a6086eea Mon Sep 17 00:00:00 2001 From: Wesley Ellis Date: Fri, 10 Jul 2026 11:24:07 -0400 Subject: [PATCH 2/2] Fix ACCOUNT_ID label lookup and add job-type label The OpsLevel API uppercases/sanitizes variable keys before sending them to the runner (RunnerJobType#format_key_as_valid_env_var_name), so the account id arrives as ACCOUNT_ID, not account_id. The previous lookup used the lowercase key and never matched, leaving opslevel.com/account-id always empty. Match the uppercase key instead. Also read a JOB_TYPE variable into an opslevel.com/job-type label so pods can be filtered by job kind (template). This is a no-op until the OpsLevel API emits JOB_TYPE as a derived variable. Co-Authored-By: Claude Opus 4.8 --- src/pkg/k8s.go | 7 ++++++- src/pkg/k8s_test.go | 21 +++++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/pkg/k8s.go b/src/pkg/k8s.go index d35c83b..9505494 100644 --- a/src/pkg/k8s.go +++ b/src/pkg/k8s.go @@ -389,9 +389,14 @@ func (s *JobRunner) Run(ctx context.Context, job opslevel.RunnerJob, stdout, std labels["app.kubernetes.io/name"] = "opslevel-job" labels["opslevel.com/job-id"] = id labels["opslevel.com/mode"] = viper.GetString("mode") - if accountId := getRunnerJobVariable(job.Variables, "account_id"); accountId != "" { + // Variable keys arrive uppercased/sanitized by the OpsLevel API (see + // RunnerJobType#format_key_as_valid_env_var_name), so match the uppercase key. + if accountId := getRunnerJobVariable(job.Variables, "ACCOUNT_ID"); accountId != "" { labels["opslevel.com/account-id"] = accountId } + if jobType := getRunnerJobVariable(job.Variables, "JOB_TYPE"); jobType != "" { + labels["opslevel.com/job-type"] = jobType + } // TODO: manage pods based on image for re-use? cfgMap, err := s.CreateConfigMap(ctx, s.getConfigMapObject(identifier, job)) if err != nil { diff --git a/src/pkg/k8s_test.go b/src/pkg/k8s_test.go index a3b8722..76d1338 100644 --- a/src/pkg/k8s_test.go +++ b/src/pkg/k8s_test.go @@ -195,13 +195,15 @@ func TestGetPodEnv_FiltersByScope(t *testing.T) { func TestGetRunnerJobVariable_ReturnsMatchingValue(t *testing.T) { // Arrange + // The OpsLevel API uppercases/sanitizes variable keys before sending them, + // so the runner matches the uppercase key. vars := []opslevel.RunnerJobVariable{ {Key: "FOO", Value: "bar"}, - {Key: "account_id", Value: "acct-123"}, + {Key: "ACCOUNT_ID", Value: "acct-123"}, } // Act - value := getRunnerJobVariable(vars, "account_id") + value := getRunnerJobVariable(vars, "ACCOUNT_ID") // Assert autopilot.Equals(t, "acct-123", value) @@ -213,6 +215,21 @@ func TestGetRunnerJobVariable_ReturnsEmptyWhenMissing(t *testing.T) { {Key: "FOO", Value: "bar"}, } + // Act + value := getRunnerJobVariable(vars, "ACCOUNT_ID") + + // Assert + autopilot.Equals(t, "", value) +} + +func TestGetRunnerJobVariable_IsCaseSensitive(t *testing.T) { + // Arrange + // Guards the bug where the runner looked up "account_id" but the API sends + // "ACCOUNT_ID"; the lowercase key must NOT match. + vars := []opslevel.RunnerJobVariable{ + {Key: "ACCOUNT_ID", Value: "acct-123"}, + } + // Act value := getRunnerJobVariable(vars, "account_id")