Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions api/v1alpha1/pipeline_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,13 @@ type Filter struct {
// +optional
Env []corev1.EnvVar `json:"env,omitempty"`

// 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"`

// args are the command arguments to pass to the container
// +optional
Args []string `json:"args,omitempty"`
Expand Down
7 changes: 7 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

52 changes: 52 additions & 0 deletions config/crd/bases/filter.plainsight.ai_pipelines.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,58 @@ spec:
- name
type: object
type: array
envFrom:
description: |-
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
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,58 @@ spec:
- name
type: object
type: array
envFrom:
description: |-
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
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
Expand Down
1 change: 1 addition & 0 deletions internal/controller/pipelineinstance_controller_batch.go
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,7 @@ func (r *PipelineInstanceReconciler) buildJob(ctx context.Context, pipelineInsta
Command: filter.Command,
Args: filter.Args,
Env: containerEnv,
EnvFrom: filter.EnvFrom,
Comment thread
nikitos marked this conversation as resolved.
ImagePullPolicy: filter.ImagePullPolicy,
VolumeMounts: []corev1.VolumeMount{
{Name: "workspace", MountPath: "/ws"},
Expand Down
37 changes: 37 additions & 0 deletions internal/controller/pipelineinstance_controller_batch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ func (r *PipelineInstanceReconciler) buildStreamingDeployment(ctx context.Contex
envVars = append(envVars, filter.Env...)

container.Env = envVars
container.EnvFrom = filter.EnvFrom
Comment thread
nikitos marked this conversation as resolved.

if filter.Resources != nil {
container.Resources = *filter.Resources
Expand Down
37 changes: 37 additions & 0 deletions internal/controller/pipelineinstance_controller_streaming_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Loading