diff --git a/.changes/unreleased/Feature-20250808-111106.yaml b/.changes/unreleased/Feature-20250808-111106.yaml new file mode 100644 index 0000000..f0c3d3c --- /dev/null +++ b/.changes/unreleased/Feature-20250808-111106.yaml @@ -0,0 +1,3 @@ +kind: Feature +body: Add ability to configure the job pod working directory using `--job-pod-workdir` or `OPSLEVEL_JOB_POD_WORKDIR` or the configuration file. +time: 2025-08-08T11:11:06.018244-04:00 diff --git a/src/cmd/root.go b/src/cmd/root.go index 6179dc2..2f84b56 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -47,6 +47,7 @@ func init() { rootCmd.PersistentFlags().Int64("job-pod-limits-cpu", 1000, "The job pod resource limits cpu millicores.") rootCmd.PersistentFlags().Int64("job-pod-limits-memory", 1024, "The job pod resource limits in MB.") rootCmd.PersistentFlags().String("job-pod-shell", "/bin/sh", "The job pod shell to use for commands run inside the pod.") + rootCmd.PersistentFlags().String("job-pod-workdir", "/jobs", "The job pod working directory.") rootCmd.PersistentFlags().Int("job-pod-log-max-interval", 30, "The max amount of time between when pod logs are shipped to OpsLevel. Works in tandem with 'job-pod-log-max-size'") rootCmd.PersistentFlags().Int("job-pod-log-max-size", 1000000, "The max amount in bytes to buffer before pod logs are shipped to OpsLevel. Works in tandem with 'job-pod-log-max-interval'") @@ -67,6 +68,7 @@ func init() { viper.BindEnv("job-pod-max-lifetime", "OPSLEVEL_JOB_POD_MAX_LIFETIME") viper.BindEnv("job-pod-namespace", "OPSLEVEL_JOB_POD_NAMESPACE") viper.BindEnv("job-pod-shell", "OPSLEVEL_JOB_POD_SHELL") + viper.BindEnv("job-pod-workdir", "OPSLEVEL_JOB_POD_WORKDIR") viper.BindEnv("job-pod-log-max-interval", "OPSLEVEL_JOB_POD_LOG_MAX_INTERVAL") viper.BindEnv("job-pod-log-max-size", "OPSLEVEL_JOB_POD_LOG_MAX_SIZE") diff --git a/src/pkg/k8s.go b/src/pkg/k8s.go index 338facc..a2df606 100644 --- a/src/pkg/k8s.go +++ b/src/pkg/k8s.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io" + "path" "strings" "time" @@ -295,9 +296,9 @@ func (s *JobRunner) Run(ctx context.Context, job opslevel.RunnerJob, stdout, std } } - workingDirectory := fmt.Sprintf("/jobs/%s/", id) + workingDirectory := path.Join(s.podConfig.WorkingDir, id) commands := append([]string{fmt.Sprintf("mkdir -p %s", workingDirectory), fmt.Sprintf("cd %s", workingDirectory), "set -xv"}, job.Commands...) - runErr := s.Exec(ctx, stdout, stderr, pod, pod.Spec.Containers[0].Name, viper.GetString("job-pod-shell"), "-e", "-c", strings.Join(commands, ";\n")) + runErr := s.Exec(ctx, stdout, stderr, pod, pod.Spec.Containers[0].Name, s.podConfig.Shell, "-e", "-c", strings.Join(commands, ";\n")) if runErr != nil { return JobOutcome{ Message: fmt.Sprintf("pod execution failed REASON: %s %s", strings.TrimSuffix(stderr.String(), "\n"), runErr), diff --git a/src/pkg/k8s_config.go b/src/pkg/k8s_config.go index 2451b92..6e28e14 100644 --- a/src/pkg/k8s_config.go +++ b/src/pkg/k8s_config.go @@ -17,6 +17,7 @@ type K8SPodConfig struct { Namespace string `yaml:"namespace"` Lifetime int `yaml:"lifetime"` // in seconds Shell string `yaml:"shell"` + WorkingDir string `yaml:"workingDir"` Annotations map[string]string `yaml:"annotations"` Resources corev1.ResourceRequirements `yaml:"resources"` ServiceAccountName string `yaml:"serviceAccountName"` @@ -30,9 +31,10 @@ type K8SPodConfig struct { func ReadPodConfig(path string) (*K8SPodConfig, error) { config := Config{ Kubernetes: K8SPodConfig{ - Namespace: viper.GetString("job-pod-namespace"), - Lifetime: viper.GetInt("job-pod-max-lifetime"), - Shell: viper.GetString("job-pod-shell"), + Namespace: viper.GetString("job-pod-namespace"), + Lifetime: viper.GetInt("job-pod-max-lifetime"), + Shell: viper.GetString("job-pod-shell"), + WorkingDir: viper.GetString("job-pod-workdir"), Resources: corev1.ResourceRequirements{ Requests: corev1.ResourceList{ corev1.ResourceCPU: *resource.NewMilliQuantity(viper.GetInt64("job-pod-requests-cpu"), resource.DecimalSI),