From 8661fbebd4a93488c5f698ca640b5e3514d68615 Mon Sep 17 00:00:00 2001 From: Nikita Andriyanov Date: Tue, 26 May 2026 15:28:09 +0300 Subject: [PATCH 1/5] add envfrom to filter spec and pass it to k8s resources --- api/v1alpha1/pipeline_types.go | 5 ++ api/v1alpha1/zz_generated.deepcopy.go | 7 +++ .../crds/filter.plainsight.ai_pipelines.yaml | 50 +++++++++++++++++++ .../bases/filter.plainsight.ai_pipelines.yaml | 50 +++++++++++++++++++ .../pipelineinstance_controller_batch.go | 1 + .../pipelineinstance_controller_streaming.go | 1 + 6 files changed, 114 insertions(+) diff --git a/api/v1alpha1/pipeline_types.go b/api/v1alpha1/pipeline_types.go index b8ea150..ce1ee2c 100644 --- a/api/v1alpha1/pipeline_types.go +++ b/api/v1alpha1/pipeline_types.go @@ -184,6 +184,11 @@ type Filter struct { // +optional Env []corev1.EnvVar `json:"env,omitempty"` + // envFrom is a source of envs to container + // Uses the standard Kubernetes EnvFromSource type for full compatibility + // +optional + EnvFrom []corev1.EnvFromSource `json:"envFrom,omitempty"` + // args are the command arguments to pass to the container // +optional Args []string `json:"args,omitempty"` diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index fc0b10c..c29883f 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -121,6 +121,13 @@ func (in *Filter) DeepCopyInto(out *Filter) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.EnvFrom != nil { + in, out := &in.EnvFrom, &out.EnvFrom + *out = make([]corev1.EnvFromSource, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } if in.Args != nil { in, out := &in.Args, &out.Args *out = make([]string, len(*in)) diff --git a/charts/openfilter-pipelines-controller/crds/filter.plainsight.ai_pipelines.yaml b/charts/openfilter-pipelines-controller/crds/filter.plainsight.ai_pipelines.yaml index 8357a30..3cd3782 100644 --- a/charts/openfilter-pipelines-controller/crds/filter.plainsight.ai_pipelines.yaml +++ b/charts/openfilter-pipelines-controller/crds/filter.plainsight.ai_pipelines.yaml @@ -242,6 +242,56 @@ spec: - name type: object type: array + envFrom: + description: |- + envFrom is a source of envs to container + Uses the standard Kubernetes EnvFromSource type for full compatibility + items: + description: EnvFromSource represents the source of a set + of ConfigMaps or Secrets + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap must be + defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: |- + Optional text to prepend to the name of each environment variable. + May consist of any printable ASCII characters except '='. + type: string + secretRef: + description: The Secret to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array image: description: image is the container image to run (e.g., "myregistry/filter:v1.0") type: string diff --git a/config/crd/bases/filter.plainsight.ai_pipelines.yaml b/config/crd/bases/filter.plainsight.ai_pipelines.yaml index 8357a30..3cd3782 100644 --- a/config/crd/bases/filter.plainsight.ai_pipelines.yaml +++ b/config/crd/bases/filter.plainsight.ai_pipelines.yaml @@ -242,6 +242,56 @@ spec: - name type: object type: array + envFrom: + description: |- + envFrom is a source of envs to container + Uses the standard Kubernetes EnvFromSource type for full compatibility + items: + description: EnvFromSource represents the source of a set + of ConfigMaps or Secrets + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap must be + defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: |- + Optional text to prepend to the name of each environment variable. + May consist of any printable ASCII characters except '='. + type: string + secretRef: + description: The Secret to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array image: description: image is the container image to run (e.g., "myregistry/filter:v1.0") type: string diff --git a/internal/controller/pipelineinstance_controller_batch.go b/internal/controller/pipelineinstance_controller_batch.go index 93e3a74..543bd63 100644 --- a/internal/controller/pipelineinstance_controller_batch.go +++ b/internal/controller/pipelineinstance_controller_batch.go @@ -513,6 +513,7 @@ func (r *PipelineInstanceReconciler) buildJob(ctx context.Context, pipelineInsta Command: filter.Command, Args: filter.Args, Env: containerEnv, + EnvFrom: filter.EnvFrom, ImagePullPolicy: filter.ImagePullPolicy, VolumeMounts: []corev1.VolumeMount{ {Name: "workspace", MountPath: "/ws"}, diff --git a/internal/controller/pipelineinstance_controller_streaming.go b/internal/controller/pipelineinstance_controller_streaming.go index 1f45ea6..2ae561c 100644 --- a/internal/controller/pipelineinstance_controller_streaming.go +++ b/internal/controller/pipelineinstance_controller_streaming.go @@ -337,6 +337,7 @@ func (r *PipelineInstanceReconciler) buildStreamingDeployment(pipelineInstance * envVars = append(envVars, filter.Env...) container.Env = envVars + container.EnvFrom = filter.EnvFrom if filter.Resources != nil { container.Resources = *filter.Resources From 3ad082d3393c67b80abb777f650a78db1ffe0c36 Mon Sep 17 00:00:00 2001 From: Nikita Andriyanov Date: Wed, 27 May 2026 08:31:20 +0300 Subject: [PATCH 2/5] fix comment --- api/v1alpha1/pipeline_types.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/api/v1alpha1/pipeline_types.go b/api/v1alpha1/pipeline_types.go index ce1ee2c..caa30bc 100644 --- a/api/v1alpha1/pipeline_types.go +++ b/api/v1alpha1/pipeline_types.go @@ -184,8 +184,10 @@ type Filter struct { // +optional Env []corev1.EnvVar `json:"env,omitempty"` - // envFrom is a source of envs to container + // envFrom is a list of sources to populate environment variables in the container. // Uses the standard Kubernetes EnvFromSource type for full compatibility + // Note: values defined in env (including those injected by the controller) + // take precedence over values from envFrom, regardless of ordering. // +optional EnvFrom []corev1.EnvFromSource `json:"envFrom,omitempty"` From b270c0630a469ba7dfcf8f3e1761657be051186c Mon Sep 17 00:00:00 2001 From: Nikita Andriyanov Date: Wed, 27 May 2026 09:46:33 +0300 Subject: [PATCH 3/5] add test --- .../pipelineinstance_controller_batch_test.go | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/internal/controller/pipelineinstance_controller_batch_test.go b/internal/controller/pipelineinstance_controller_batch_test.go index 651623f..a9bfe3d 100644 --- a/internal/controller/pipelineinstance_controller_batch_test.go +++ b/internal/controller/pipelineinstance_controller_batch_test.go @@ -1161,3 +1161,40 @@ func TestBuildJob_StreamKeyUsesNamespacePrefix(t *testing.T) { } t.Error("expected STREAM env var in claimer, not found") } + +func TestBuildJob_EnvFrom_Propagated(t *testing.T) { + r := makeMinimalReconciler() + pi := makeMinimalPipelineInstance() + ps := makeMinimalPipelineSource() + + pipeline := &pipelinesv1alpha1.Pipeline{ + Spec: pipelinesv1alpha1.PipelineSpec{ + Filters: []pipelinesv1alpha1.Filter{ + { + Name: "f-with-envfrom", + Image: "image:latest", + EnvFrom: []corev1.EnvFromSource{ + {ConfigMapRef: &corev1.ConfigMapEnvSource{LocalObjectReference: corev1.LocalObjectReference{Name: "my-config"}}}, + {SecretRef: &corev1.SecretEnvSource{LocalObjectReference: corev1.LocalObjectReference{Name: "my-secret"}}}, + }, + }, + }, + }, + } + + job := r.buildJob(context.Background(), pi, pipeline, ps, "test-job") + containers := job.Spec.Template.Spec.Containers + if len(containers) == 0 { + t.Fatal("expected at least one container") + } + ef := containers[0].EnvFrom + if len(ef) != 2 { + t.Fatalf("expected 2 EnvFrom entries, got %d: %v", len(ef), ef) + } + if ef[0].ConfigMapRef == nil || ef[0].ConfigMapRef.Name != "my-config" { + t.Errorf("expected first EnvFrom to be ConfigMapRef my-config, got %v", ef[0].ConfigMapRef) + } + if ef[1].SecretRef == nil || ef[1].SecretRef.Name != "my-secret" { + t.Errorf("expected second EnvFrom to be SecretRef my-secret, got %v", ef[1].SecretRef) + } +} From f4da8dd5e548b89ca2067d0fede4cf0caa03c0d0 Mon Sep 17 00:00:00 2001 From: Nikita Andriyanov Date: Wed, 27 May 2026 09:56:00 +0300 Subject: [PATCH 4/5] add more tests --- ...elineinstance_controller_streaming_test.go | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/internal/controller/pipelineinstance_controller_streaming_test.go b/internal/controller/pipelineinstance_controller_streaming_test.go index 856c549..0935025 100644 --- a/internal/controller/pipelineinstance_controller_streaming_test.go +++ b/internal/controller/pipelineinstance_controller_streaming_test.go @@ -923,3 +923,40 @@ func TestBuildRTSPURLWithCredentials(t *testing.T) { }) } } + +func TestBuildStreamingDeployment_EnvFrom_Propagated(t *testing.T) { + r := makeMinimalReconciler() + pi := makeMinimalPipelineInstance() + ps := makeMinimalPipelineSource() + + pipeline := &pipelinesv1alpha1.Pipeline{ + Spec: pipelinesv1alpha1.PipelineSpec{ + Filters: []pipelinesv1alpha1.Filter{ + { + Name: "f-with-envfrom", + Image: "image:latest", + EnvFrom: []corev1.EnvFromSource{ + {ConfigMapRef: &corev1.ConfigMapEnvSource{LocalObjectReference: corev1.LocalObjectReference{Name: "my-config"}}}, + {SecretRef: &corev1.SecretEnvSource{LocalObjectReference: corev1.LocalObjectReference{Name: "my-secret"}}}, + }, + }, + }, + }, + } + + dep := r.buildStreamingDeployment(context.Background(), pi, pipeline, ps, "test-deploy") + containers := dep.Spec.Template.Spec.Containers + if len(containers) == 0 { + t.Fatal("expected at least one container") + } + ef := containers[0].EnvFrom + if len(ef) != 2 { + t.Fatalf("expected 2 EnvFrom entries, got %d: %v", len(ef), ef) + } + if ef[0].ConfigMapRef == nil || ef[0].ConfigMapRef.Name != "my-config" { + t.Errorf("expected first EnvFrom to be ConfigMapRef my-config, got %v", ef[0].ConfigMapRef) + } + if ef[1].SecretRef == nil || ef[1].SecretRef.Name != "my-secret" { + t.Errorf("expected second EnvFrom to be SecretRef my-secret, got %v", ef[1].SecretRef) + } +} From 00a79cd36e7efe5c356ff45c00d11e213cab1655 Mon Sep 17 00:00:00 2001 From: Nikita Andriyanov Date: Wed, 27 May 2026 10:09:43 +0300 Subject: [PATCH 5/5] fix crds --- api/v1alpha1/pipeline_types.go | 2 +- config/crd/bases/filter.plainsight.ai_pipelines.yaml | 4 +++- .../crds/filter.plainsight.ai_pipelines.yaml | 4 +++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/api/v1alpha1/pipeline_types.go b/api/v1alpha1/pipeline_types.go index caa30bc..014f990 100644 --- a/api/v1alpha1/pipeline_types.go +++ b/api/v1alpha1/pipeline_types.go @@ -184,7 +184,7 @@ type Filter struct { // +optional Env []corev1.EnvVar `json:"env,omitempty"` - // envFrom is a list of sources to populate environment variables in the container. + // envFrom is a list of sources to populate environment variables in the container. // Uses the standard Kubernetes EnvFromSource type for full compatibility // Note: values defined in env (including those injected by the controller) // take precedence over values from envFrom, regardless of ordering. diff --git a/config/crd/bases/filter.plainsight.ai_pipelines.yaml b/config/crd/bases/filter.plainsight.ai_pipelines.yaml index 3cd3782..17a6daf 100644 --- a/config/crd/bases/filter.plainsight.ai_pipelines.yaml +++ b/config/crd/bases/filter.plainsight.ai_pipelines.yaml @@ -244,8 +244,10 @@ spec: type: array envFrom: description: |- - envFrom is a source of envs to container + envFrom is a list of sources to populate environment variables in the container. Uses the standard Kubernetes EnvFromSource type for full compatibility + Note: values defined in env (including those injected by the controller) + take precedence over values from envFrom, regardless of ordering. items: description: EnvFromSource represents the source of a set of ConfigMaps or Secrets diff --git a/deployment/openfilter-pipelines-controller/crds/filter.plainsight.ai_pipelines.yaml b/deployment/openfilter-pipelines-controller/crds/filter.plainsight.ai_pipelines.yaml index 3cd3782..17a6daf 100644 --- a/deployment/openfilter-pipelines-controller/crds/filter.plainsight.ai_pipelines.yaml +++ b/deployment/openfilter-pipelines-controller/crds/filter.plainsight.ai_pipelines.yaml @@ -244,8 +244,10 @@ spec: type: array envFrom: description: |- - envFrom is a source of envs to container + envFrom is a list of sources to populate environment variables in the container. Uses the standard Kubernetes EnvFromSource type for full compatibility + Note: values defined in env (including those injected by the controller) + take precedence over values from envFrom, regardless of ordering. items: description: EnvFromSource represents the source of a set of ConfigMaps or Secrets