diff --git a/.changes/unreleased/Feature-20260706-113000.yaml b/.changes/unreleased/Feature-20260706-113000.yaml new file mode 100644 index 00000000..6851c8fd --- /dev/null +++ b/.changes/unreleased/Feature-20260706-113000.yaml @@ -0,0 +1,3 @@ +kind: Feature +body: Add --job-pod-requests-ephemeral-storage and --job-pod-limits-ephemeral-storage flags (in MB) to set the job pod's ephemeral-storage request and limit; both default to unset +time: 2026-07-06T11:30:00.000000000Z diff --git a/src/cmd/root.go b/src/cmd/root.go index 006794fa..b9afdfec 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -46,6 +46,8 @@ func init() { rootCmd.PersistentFlags().Int64("job-pod-requests-memory", 1024, "The job pod resource requests in MB.") 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().Int64("job-pod-requests-ephemeral-storage", 0, "The job pod resource requests ephemeral storage in MB. 0 leaves the request unset.") + rootCmd.PersistentFlags().Int64("job-pod-limits-ephemeral-storage", 0, "The job pod resource limits ephemeral storage in MB. 0 leaves the limit unset.") 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'") diff --git a/src/pkg/k8s_config.go b/src/pkg/k8s_config.go index e532c31c..5538bb8a 100644 --- a/src/pkg/k8s_config.go +++ b/src/pkg/k8s_config.go @@ -53,6 +53,14 @@ func ReadPodConfig(path string) (*K8SPodConfig, error) { HelperImage: viper.GetString("job-pod-helper-image"), }, } + // Ephemeral storage is only set when configured so existing deployments are unaffected; + // a literal 0 limit would kill any pod that writes to disk. + if v := viper.GetInt64("job-pod-requests-ephemeral-storage"); v > 0 { + config.Kubernetes.Resources.Requests[corev1.ResourceEphemeralStorage] = *resource.NewQuantity(v*1024*1024, resource.BinarySI) + } + if v := viper.GetInt64("job-pod-limits-ephemeral-storage"); v > 0 { + config.Kubernetes.Resources.Limits[corev1.ResourceEphemeralStorage] = *resource.NewQuantity(v*1024*1024, resource.BinarySI) + } // Early out with viper defaults if config file doesn't exist if _, err := os.Stat(path); os.IsNotExist(err) { return &config.Kubernetes, nil diff --git a/src/pkg/k8s_config_test.go b/src/pkg/k8s_config_test.go new file mode 100644 index 00000000..ac5c78fe --- /dev/null +++ b/src/pkg/k8s_config_test.go @@ -0,0 +1,39 @@ +package pkg + +import ( + "testing" + + "github.com/rocktavious/autopilot/v2023" + "github.com/spf13/viper" + corev1 "k8s.io/api/core/v1" +) + +func TestReadPodConfig_EphemeralStorageUnsetByDefault(t *testing.T) { + // Arrange + viper.Reset() + t.Cleanup(viper.Reset) + // Act + config, err := ReadPodConfig("does-not-exist.yaml") + // Assert + autopilot.Ok(t, err) + _, hasRequest := config.Resources.Requests[corev1.ResourceEphemeralStorage] + _, hasLimit := config.Resources.Limits[corev1.ResourceEphemeralStorage] + autopilot.Equals(t, false, hasRequest) + autopilot.Equals(t, false, hasLimit) +} + +func TestReadPodConfig_EphemeralStorageSet(t *testing.T) { + // Arrange + viper.Reset() + t.Cleanup(viper.Reset) + viper.Set("job-pod-requests-ephemeral-storage", 5120) + viper.Set("job-pod-limits-ephemeral-storage", 20480) + // Act + config, err := ReadPodConfig("does-not-exist.yaml") + // Assert + autopilot.Ok(t, err) + request := config.Resources.Requests[corev1.ResourceEphemeralStorage] + limit := config.Resources.Limits[corev1.ResourceEphemeralStorage] + autopilot.Equals(t, "5Gi", request.String()) + autopilot.Equals(t, "20Gi", limit.String()) +}