From d515a530ce84bbadd9008760d583135c62227cf3 Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Thu, 30 Oct 2025 15:27:19 +0200 Subject: [PATCH 01/30] feat: configure init containers Signed-off-by: Oleksii Kurinnyi --- .../devworkspaceoperatorconfig_types.go | 4 + .../workspace/devworkspace_controller.go | 129 ++++++++++++++++++ docs/dwo-configuration.md | 80 +++++++++++ pkg/config/sync.go | 15 ++ pkg/library/home/persistentHome.go | 32 ++--- pkg/library/home/util.go | 37 +++++ 6 files changed, 280 insertions(+), 17 deletions(-) create mode 100644 pkg/library/home/util.go diff --git a/apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go b/apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go index 9ffff832f..a1dd397fe 100644 --- a/apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go +++ b/apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go @@ -203,6 +203,10 @@ type WorkspaceConfig struct { // If the feature is disabled, setting this field may cause an endless workspace start loop. // +kubebuilder:validation:Optional HostUsers *bool `json:"hostUsers,omitempty"` + // InitContainers are injected into all workspace pods as Kubernetes init containers. + // Typical uses: injecting organization tools/configs, initializing persistent home, etc. + // Note: Only trusted administrators should be allowed to edit the DevWorkspaceOperatorConfig. + InitContainers []corev1.Container `json:"initContainers,omitempty"` } type WebhookConfig struct { diff --git a/controllers/workspace/devworkspace_controller.go b/controllers/workspace/devworkspace_controller.go index e158e69ad..b645e0284 100644 --- a/controllers/workspace/devworkspace_controller.go +++ b/controllers/workspace/devworkspace_controller.go @@ -67,6 +67,107 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" ) +// applyHomeInitDefaults applies default values for image and command fields +// of the init-persistent-home container. +func applyHomeInitDefaults(c corev1.Container, workspace *common.DevWorkspaceWithConfig) (corev1.Container, error) { + if c.Image == "" { + inferred := home.InferWorkspaceImage(&workspace.Spec.Template) + if inferred == "" { + return c, fmt.Errorf("unable to infer workspace image for init-persistent-home; specify image explicitly") + } + c.Image = inferred + } + if len(c.Command) == 0 { + c.Command = []string{"/bin/sh", "-c"} + } + return c, nil +} + +// validateNoAdvancedFields validates that the init-persistent-home container +// does not use advanced Kubernetes container fields that could make behavior unpredictable. +func validateNoAdvancedFields(c corev1.Container) error { + if len(c.Ports) > 0 { + return fmt.Errorf("ports are not allowed for init-persistent-home") + } + + if c.LivenessProbe != nil || c.ReadinessProbe != nil || c.StartupProbe != nil { + return fmt.Errorf("probes are not allowed for init-persistent-home") + } + + if c.Lifecycle != nil { + return fmt.Errorf("lifecycle hooks are not allowed for init-persistent-home") + } + + if c.Stdin || c.StdinOnce || c.TTY { + return fmt.Errorf("stdin/tty fields are not allowed for init-persistent-home") + } + + if len(c.VolumeDevices) > 0 || c.WorkingDir != "" { + return fmt.Errorf("volumeDevices and workingDir are not allowed for init-persistent-home") + } + + if c.TerminationMessagePath != "" || c.TerminationMessagePolicy != "" { + return fmt.Errorf("termination message fields are not allowed for init-persistent-home") + } + + if c.SecurityContext != nil { + return fmt.Errorf("securityContext is not allowed for init-persistent-home") + } + if c.Resources.Limits != nil || c.Resources.Requests != nil { + return fmt.Errorf("resource limits/requests are not allowed for init-persistent-home") + } + + return nil +} + +// validateHomeInitContainer validates all aspects of the init-persistent-home container. +func validateHomeInitContainer(c corev1.Container) error { + if strings.ContainsAny(c.Image, "\n\r\t ") { + return fmt.Errorf("invalid image reference for init-persistent-home: image reference contains invalid whitespace characters") + } + + if len(c.Command) != 2 || c.Command[0] != "/bin/sh" || c.Command[1] != "-c" { + return fmt.Errorf("command must be exactly [/bin/sh, -c]") + } + + if len(c.Args) != 1 { + return fmt.Errorf("args must contain exactly one script string") + } + + if len(c.VolumeMounts) > 0 { + return fmt.Errorf("volumeMounts are not allowed for init-persistent-home; persistent-home is auto-mounted at /home/user/") + } + + if err := validateNoAdvancedFields(c); err != nil { + return err + } + + return nil +} + +// defaultAndValidateHomeInitContainer applies defaults and validation for a custom +// DWOC-provided init container named init-persistent-home. It ensures a shell execution +// model, a single script arg, injects the persistent-home mount at /home/user/, and +// defaults image to the inferred workspace image if not provided. +func defaultAndValidateHomeInitContainer(c corev1.Container, workspace *common.DevWorkspaceWithConfig) (corev1.Container, error) { + var err error + + if c, err = applyHomeInitDefaults(c, workspace); err != nil { + return c, err + } + + if err = validateHomeInitContainer(c); err != nil { + return c, err + } + + c.VolumeMounts = []corev1.VolumeMount{{ + Name: constants.HomeVolumeName, + MountPath: constants.HomeUserDirectory, + }} + + return c, nil +} + const ( startingWorkspaceRequeueInterval = 5 * time.Second ) @@ -368,6 +469,34 @@ func (r *DevWorkspaceReconciler) Reconcile(ctx context.Context, req ctrl.Request devfilePodAdditions.InitContainers = append([]corev1.Container{*projectClone}, devfilePodAdditions.InitContainers...) } + // Inject operator-configured init containers + if workspace.Config != nil && workspace.Config.Workspace != nil { + // Check if init-persistent-home should be disabled + disableHomeInit := workspace.Config.Workspace.PersistUserHome.DisableInitContainer != nil && + *workspace.Config.Workspace.PersistUserHome.DisableInitContainer + + for _, c := range workspace.Config.Workspace.InitContainers { + // Special handling for init-persistent-home + if c.Name == constants.HomeInitComponentName { + // Skip if persistent home is disabled + if !home.PersistUserHomeEnabled(workspace) { + continue + } + // Skip if init container is explicitly disabled + if disableHomeInit { + continue + } + // Apply defaults and validation for init-persistent-home + validated, err := defaultAndValidateHomeInitContainer(c, workspace) + if err != nil { + return r.failWorkspace(workspace, fmt.Sprintf("Invalid init-persistent-home container: %s", err), metrics.ReasonBadRequest, reqLogger, &reconcileStatus), nil + } + c = validated + } + devfilePodAdditions.InitContainers = append(devfilePodAdditions.InitContainers, c) + } + } + // Add ServiceAccount tokens into devfile containers if err := wsprovision.ProvisionServiceAccountTokensInto(devfilePodAdditions, workspace); err != nil { return r.failWorkspace(workspace, fmt.Sprintf("Failed to mount ServiceAccount tokens to workspace: %s", err), metrics.ReasonBadRequest, reqLogger, &reconcileStatus), nil diff --git a/docs/dwo-configuration.md b/docs/dwo-configuration.md index 66d6228ff..82ac55a7b 100644 --- a/docs/dwo-configuration.md +++ b/docs/dwo-configuration.md @@ -125,3 +125,83 @@ config: ``` The config above will have newly created PVCs to have its access mode set to `ReadWriteMany`. + +## Configuring Custom Init Containers + +The DevWorkspace Operator allows cluster administrators to inject custom init containers into all workspace pods via the `config.workspace.initContainers` field in the global DWOC. This feature enables use cases such as: + +- Injecting organization-specific tools or configurations +- Customizing the persistent home directory initialization logic +- Extracting cluster utilities (e.g., `oc` CLI) to ensure version compatibility + +**Security Note:** Only trusted administrators should have RBAC permissions to edit the `DevWorkspaceOperatorConfig`, as custom init containers run in every workspace and can execute arbitrary code. + +### Basic Example: Injecting Custom Tools + +```yaml +apiVersion: controller.devfile.io/v1alpha1 +kind: DevWorkspaceOperatorConfig +metadata: + name: devworkspace-operator-config + namespace: $OPERATOR_INSTALL_NAMESPACE +config: + workspace: + initContainers: + - name: inject-oc-cli + image: registry.redhat.io/openshift4/ose-cli:latest + command: ["/bin/sh", "-c"] + args: + - | + cp /usr/bin/oc /home/user/bin/oc + cp /usr/bin/kubectl /home/user/bin/kubectl + volumeMounts: + - name: persistent-home + mountPath: /home/user/ +``` + +### Special Container: `init-persistent-home` + +A specially-named init container `init-persistent-home` can be used to override the built-in persistent home directory initialization logic when `config.workspace.persistUserHome.enabled: true`. This is useful for enterprises using customized UDI images that require different home directory setup logic. + +**Contract for `init-persistent-home`:** + +- **Name:** Must be exactly `init-persistent-home` +- **Image:** Optional. If omitted, defaults to the first non-imported workspace container's image. If no suitable image can be inferred, the workspace will fail to start with an error. +- **Command:** Optional. If omitted, defaults to `["/bin/sh", "-c"]`. If provided, must exactly match this value. +- **Args:** Required. Must contain exactly one string with the initialization script. +- **VolumeMounts:** Forbidden. The operator automatically mounts the `persistent-home` volume at `/home/user/`. +- **Env:** Optional. Environment variables are allowed. +- **Other fields:** Not allowed. Fields such as `ports`, `probes`, `lifecycle`, `securityContext`, `resources`, `volumeDevices`, `stdin`, `tty`, and `workingDir` are rejected to keep behavior predictable. + +**Note:** If `persistUserHome.enabled` is `false`, any `init-persistent-home` container is ignored. + +### Example: Custom Persistent Home Initialization + +```yaml +apiVersion: controller.devfile.io/v1alpha1 +kind: DevWorkspaceOperatorConfig +metadata: + name: devworkspace-operator-config + namespace: $OPERATOR_INSTALL_NAMESPACE +config: + workspace: + persistUserHome: + enabled: true + initContainers: + - name: init-persistent-home + # image: optional - defaults to workspace image + # command: optional - defaults to ["/bin/sh", "-c"] + args: + - | + echo "Enterprise home init" + # Custom logic for enterprise UDI + rsync -a --ignore-existing /home/tooling/ /home/user/ || true + touch /home/user/.home_initialized + env: + - name: CUSTOM_VAR + value: "custom-value" +``` + +### Execution Order + +Custom init containers are injected after the project-clone init container in the order they are defined in the configuration. The `init-persistent-home` container runs in this sequence along with other custom init containers. diff --git a/pkg/config/sync.go b/pkg/config/sync.go index 76ffd89a5..7b62e06c2 100644 --- a/pkg/config/sync.go +++ b/pkg/config/sync.go @@ -439,6 +439,14 @@ func mergeConfig(from, to *controller.OperatorConfiguration) { if from.Workspace.HostUsers != nil { to.Workspace.HostUsers = from.Workspace.HostUsers } + + if from.Workspace.InitContainers != nil { + initContainersCopy := make([]corev1.Container, len(from.Workspace.InitContainers)) + for i, container := range from.Workspace.InitContainers { + initContainersCopy[i] = *container.DeepCopy() + } + to.Workspace.InitContainers = initContainersCopy + } } } @@ -687,6 +695,13 @@ func GetCurrentConfigString(currConfig *controller.OperatorConfiguration) string if workspace.HostUsers != nil { config = append(config, fmt.Sprintf("workspace.hostUsers=%t", *workspace.HostUsers)) } + if len(workspace.InitContainers) > 0 { + initContainerNames := make([]string, len(workspace.InitContainers)) + for i, container := range workspace.InitContainers { + initContainerNames[i] = container.Name + } + config = append(config, fmt.Sprintf("workspace.initContainers=[%s]", strings.Join(initContainerNames, ", "))) + } } if currConfig.EnableExperimentalFeatures != nil && *currConfig.EnableExperimentalFeatures { config = append(config, "enableExperimentalFeatures=true") diff --git a/pkg/library/home/persistentHome.go b/pkg/library/home/persistentHome.go index bb6d8132b..c6e0a9cc8 100644 --- a/pkg/library/home/persistentHome.go +++ b/pkg/library/home/persistentHome.go @@ -62,7 +62,19 @@ func AddPersistentHomeVolume(workspace *common.DevWorkspaceWithConfig) (*v1alpha Path: constants.HomeUserDirectory, } - if workspace.Config.Workspace.PersistUserHome.DisableInitContainer == nil || !*workspace.Config.Workspace.PersistUserHome.DisableInitContainer { + // Determine if a custom home init container is configured via DWOC + hasCustomHomeInit := false + if workspace.Config != nil && workspace.Config.Workspace != nil { + for _, c := range workspace.Config.Workspace.InitContainers { + if c.Name == constants.HomeInitComponentName { + hasCustomHomeInit = true + break + } + } + } + + // Add default init container only if not disabled and no custom init is configured + if (workspace.Config.Workspace.PersistUserHome.DisableInitContainer == nil || !*workspace.Config.Workspace.PersistUserHome.DisableInitContainer) && !hasCustomHomeInit { err := addInitContainer(dwTemplateSpecCopy) if err != nil { return nil, fmt.Errorf("failed to add init container for home persistence setup: %w", err) @@ -216,22 +228,8 @@ func addInitContainerComponent(dwTemplateSpec *v1alpha2.DevWorkspaceTemplateSpec } func inferInitContainer(dwTemplateSpec *v1alpha2.DevWorkspaceTemplateSpec) *v1alpha2.Container { - var nonImportedComponent v1alpha2.Component - for _, component := range dwTemplateSpec.Components { - if component.Container == nil { - continue - } - - pluginSource := component.Attributes.GetString(constants.PluginSourceAttribute, nil) - if pluginSource == "" || pluginSource == "parent" { - // First non-imported container component is selected - nonImportedComponent = component - break - } - } - - if nonImportedComponent.Name != "" { - image := nonImportedComponent.Container.Image + image := InferWorkspaceImage(dwTemplateSpec) + if image != "" { return &v1alpha2.Container{ Image: image, Command: []string{"/bin/sh", "-c"}, diff --git a/pkg/library/home/util.go b/pkg/library/home/util.go new file mode 100644 index 000000000..521624cb2 --- /dev/null +++ b/pkg/library/home/util.go @@ -0,0 +1,37 @@ +// +// Copyright (c) 2019-2025 Red Hat, Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package home + +import ( + "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" + "github.com/devfile/devworkspace-operator/pkg/constants" +) + +// InferWorkspaceImage finds the first non-imported container component image in the +// flattened devfile template. This mirrors the selection rule used by the built-in +// persistent-home initializer to pick a "primary" workspace image. +func InferWorkspaceImage(dwTemplate *v1alpha2.DevWorkspaceTemplateSpec) string { + for _, component := range dwTemplate.Components { + if component.Container == nil { + continue + } + pluginSource := component.Attributes.GetString(constants.PluginSourceAttribute, nil) + if pluginSource == "" || pluginSource == "parent" { + return component.Container.Image + } + } + return "" +} From c944db0522dab3bb24476973b1183bba866ed60a Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Thu, 30 Oct 2025 15:36:28 +0200 Subject: [PATCH 02/30] chore: run make update_devworkspace_api update_devworkspace_crds generate_all Signed-off-by: Oleksii Kurinnyi --- .../v1alpha1/zz_generated.deepcopy.go | 7 + ...evfile.io_devworkspaceoperatorconfigs.yaml | 1465 ++++++++++++++++ deploy/deployment/kubernetes/combined.yaml | 1533 +++++++++++++++++ ...r.devfile.io.CustomResourceDefinition.yaml | 1533 +++++++++++++++++ deploy/deployment/openshift/combined.yaml | 1533 +++++++++++++++++ ...r.devfile.io.CustomResourceDefinition.yaml | 1533 +++++++++++++++++ ...evfile.io_devworkspaceoperatorconfigs.yaml | 1533 +++++++++++++++++ 7 files changed, 9137 insertions(+) diff --git a/apis/controller/v1alpha1/zz_generated.deepcopy.go b/apis/controller/v1alpha1/zz_generated.deepcopy.go index f31eb7604..0628b07dd 100644 --- a/apis/controller/v1alpha1/zz_generated.deepcopy.go +++ b/apis/controller/v1alpha1/zz_generated.deepcopy.go @@ -793,6 +793,13 @@ func (in *WorkspaceConfig) DeepCopyInto(out *WorkspaceConfig) { *out = new(bool) **out = **in } + if in.InitContainers != nil { + in, out := &in.InitContainers, &out.InitContainers + *out = make([]v1.Container, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceConfig. diff --git a/deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml b/deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml index 471d552c8..3577551b5 100644 --- a/deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml +++ b/deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml @@ -2425,6 +2425,1471 @@ spec: - Always - Never type: string + initContainers: + description: |- + InitContainers are injected into all workspace pods as Kubernetes init containers. + Typical uses: injecting organization tools/configs, initializing persistent home, etc. + Note: Only trusted administrators should be allowed to edit the DevWorkspaceOperatorConfig. + items: + description: A single application container that you want to run within a pod. + properties: + args: + description: |- + Arguments to the entrypoint. + The container image's CMD is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + description: |- + Entrypoint array. Not executed within a shell. + The container image's ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + description: |- + List of environment variables to set in the container. + Cannot be updated. + items: + description: EnvVar represents an environment variable present in a Container. + properties: + name: + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + 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 or its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + 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 or its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + description: |- + List of sources to populate environment variables in the container. + The keys defined within a source may consist of any printable ASCII characters except '='. + When a key exists in multiple + sources, the value associated with the last source will take precedence. + Values defined by an Env with a duplicate key will take precedence. + Cannot be updated. + 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 + x-kubernetes-list-type: atomic + image: + description: |- + Container image name. + More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management to default or override + container images in workload controllers like Deployments and StatefulSets. + type: string + imagePullPolicy: + description: |- + Image pull policy. + One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/containers/images#updating-images + type: string + lifecycle: + description: |- + Actions that the management system should take in response to container lifecycle events. + Cannot be updated. + properties: + postStart: + description: |- + PostStart is called immediately after a container is created. If the handler fails, + the container is terminated and restarted according to its restart policy. + Other management of the container blocks until the hook completes. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks + properties: + exec: + description: Exec specifies a command to execute in the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents a duration that the container should sleep. + properties: + seconds: + description: Seconds is the number of seconds to sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for backward compatibility. There is no validation of this field and + lifecycle hooks will fail at runtime when it is specified. + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: |- + PreStop is called immediately before a container is terminated due to an + API request or management event such as liveness/startup probe failure, + preemption, resource contention, etc. The handler is not called if the + container crashes or exits. The Pod's termination grace period countdown begins before the + PreStop hook is executed. Regardless of the outcome of the handler, the + container will eventually terminate within the Pod's termination grace + period (unless delayed by finalizers). Other management of the container blocks until the hook completes + or until the termination grace period is reached. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks + properties: + exec: + description: Exec specifies a command to execute in the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents a duration that the container should sleep. + properties: + seconds: + description: Seconds is the number of seconds to sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for backward compatibility. There is no validation of this field and + lifecycle hooks will fail at runtime when it is specified. + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + description: |- + StopSignal defines which signal will be sent to a container when it is being stopped. + If not specified, the default is defined by the container runtime in use. + StopSignal can only be set for Pods with a non-empty .spec.os.name + type: string + type: object + livenessProbe: + description: |- + Periodic probe of container liveness. + Container will be restarted if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + name: + description: |- + Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: |- + List of ports to expose from the container. Not specifying a port here + DOES NOT prevent that port from being exposed. Any port which is + listening on the default "0.0.0.0" address inside a container will be + accessible from the network. + Modifying this array with strategic merge patch may corrupt the data. + For more information See https://github.com/kubernetes/kubernetes/issues/108255. + Cannot be updated. + items: + description: ContainerPort represents a network port in a single container. + properties: + containerPort: + description: |- + Number of port to expose on the pod's IP address. + This must be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port to. + type: string + hostPort: + description: |- + Number of port to expose on the host. + If specified, this must be a valid port number, 0 < x < 65536. + If HostNetwork is specified, this must match ContainerPort. + Most containers do not need this. + format: int32 + type: integer + name: + description: |- + If specified, this must be an IANA_SVC_NAME and unique within the pod. Each + named port in a pod must have a unique name. Name for the port that can be + referred to by services. + type: string + protocol: + default: TCP + description: |- + Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: |- + Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + resizePolicy: + description: Resources resize policy for the container. + items: + description: ContainerResizePolicy represents resource resize policy for the container. + properties: + resourceName: + description: |- + Name of the resource to which this resource resize policy applies. + Supported values: cpu, memory. + type: string + restartPolicy: + description: |- + Restart policy to apply when specified resource is resized. + If not specified, it defaults to NotRequired. + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + description: |- + Compute Resources required by this container. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This field depends on the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + restartPolicy: + description: |- + RestartPolicy defines the restart behavior of individual containers in a pod. + This overrides the pod-level restart policy. When this field is not specified, + the restart behavior is defined by the Pod's restart policy and the container type. + Additionally, setting the RestartPolicy as "Always" for the init container will + have the following effect: + this init container will be continually restarted on + exit until all regular containers have terminated. Once all regular + containers have completed, all init containers with restartPolicy "Always" + will be shut down. This lifecycle differs from normal init containers and + is often referred to as a "sidecar" container. Although this init + container still starts in the init container sequence, it does not wait + for the container to complete before proceeding to the next init + container. Instead, the next init container starts immediately after this + init container is started, or after any startupProbe has successfully + completed. + type: string + restartPolicyRules: + description: |- + Represents a list of rules to be checked to determine if the + container should be restarted on exit. The rules are evaluated in + order. Once a rule matches a container exit condition, the remaining + rules are ignored. If no rule matches the container exit condition, + the Container-level restart policy determines the whether the container + is restarted or not. Constraints on the rules: + - At most 20 rules are allowed. + - Rules can have the same action. + - Identical rules are not forbidden in validations. + When rules are specified, container MUST set RestartPolicy explicitly + even it if matches the Pod's RestartPolicy. + items: + description: ContainerRestartRule describes how a container exit is handled. + properties: + action: + description: |- + Specifies the action taken on a container exit if the requirements + are satisfied. The only possible value is "Restart" to restart the + container. + type: string + exitCodes: + description: Represents the exit codes to check on container exits. + properties: + operator: + description: |- + Represents the relationship between the container exit code(s) and the + specified values. Possible values are: + - In: the requirement is satisfied if the container exit code is in the + set of specified values. + - NotIn: the requirement is satisfied if the container exit code is + not in the set of specified values. + type: string + values: + description: |- + Specifies the set of values to check for container exit codes. + At most 255 elements are allowed. + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic + securityContext: + description: |- + SecurityContext defines the security options the container should be run with. + If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities type + type: string + type: array + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities type + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: |- + procMount denotes the type of proc mount to use for the containers. + The default value is Default which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies to the container. + type: string + role: + description: Role is a SELinux role label that applies to the container. + type: string + type: + description: Type is a SELinux type label that applies to the container. + type: string + user: + description: User is a SELinux user label that applies to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + startupProbe: + description: |- + StartupProbe indicates that the Pod has successfully initialized. + If specified, no other probes are executed until this completes successfully. + If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. + This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, + when it might take a long time to load data or warm a cache, than during steady-state operation. + This cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + stdin: + description: |- + Whether this container should allocate a buffer for stdin in the container runtime. If this + is not set, reads from stdin in the container will always result in EOF. + Default is false. + type: boolean + stdinOnce: + description: |- + Whether the container runtime should close the stdin channel after it has been opened by + a single attach. When stdin is true the stdin stream will remain open across multiple attach + sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the + first client attaches to stdin, and then remains open and accepts data until the client disconnects, + at which time stdin is closed and remains closed until the container is restarted. If this + flag is false, a container processes that reads from stdin will never receive an EOF. + Default is false + type: boolean + terminationMessagePath: + description: |- + Optional: Path at which the file to which the container's termination message + will be written is mounted into the container's filesystem. + Message written is intended to be brief final status, such as an assertion failure message. + Will be truncated by the node if greater than 4096 bytes. The total message length across + all containers will be limited to 12kb. + Defaults to /dev/termination-log. + Cannot be updated. + type: string + terminationMessagePolicy: + description: |- + Indicate how the termination message should be populated. File will use the contents of + terminationMessagePath to populate the container status message on both success and failure. + FallbackToLogsOnError will use the last chunk of container log output if the termination + message file is empty and the container exited with an error. + The log output is limited to 2048 bytes or 80 lines, whichever is smaller. + Defaults to File. + Cannot be updated. + type: string + tty: + description: |- + Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. + Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices to be used by the container. + items: + description: volumeDevice describes a mapping of a raw block device within a container. + properties: + devicePath: + description: devicePath is the path inside of the container that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim in the pod + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + description: |- + Pod volumes to mount into the container's filesystem. + Cannot be updated. + items: + description: VolumeMount describes a mounting of a Volume within a container. + properties: + mountPath: + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: |- + mountPropagation determines how mounts are propagated from the host + to container and the other way around. + When not set, MountPropagationNone is used. + This field is beta in 1.10. + When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + (which defaults to None). + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: |- + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. + type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly specifies whether read-only mounts should be handled + recursively. + + If ReadOnly is false, this field has no meaning and must be unspecified. + + If ReadOnly is true, and this field is set to Disabled, the mount is not made + recursively read-only. If this field is set to IfPossible, the mount is made + recursively read-only, if it is supported by the container runtime. If this + field is set to Enabled, the mount is made recursively read-only if it is + supported by the container runtime, otherwise the pod will not be started and + an error will be generated to indicate the reason. + + If this field is set to IfPossible or Enabled, MountPropagation must be set to + None (or be unspecified, which defaults to None). + + If this field is not specified, it is treated as an equivalent of Disabled. + type: string + subPath: + description: |- + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + subPathExpr: + description: |- + Expanded path within the volume from which the container's volume should be mounted. + Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. + Defaults to "" (volume's root). + SubPathExpr and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + description: |- + Container's working directory. + If not specified, the container runtime's default will be used, which + might be configured in the container image. + Cannot be updated. + type: string + required: + - name + type: object + type: array persistUserHome: description: |- PersistUserHome defines configuration options for persisting the `/home/user/` diff --git a/deploy/deployment/kubernetes/combined.yaml b/deploy/deployment/kubernetes/combined.yaml index 34953ec1e..69ccbf682 100644 --- a/deploy/deployment/kubernetes/combined.yaml +++ b/deploy/deployment/kubernetes/combined.yaml @@ -2555,6 +2555,1539 @@ spec: - Always - Never type: string + initContainers: + description: |- + InitContainers are injected into all workspace pods as Kubernetes init containers. + Typical uses: injecting organization tools/configs, initializing persistent home, etc. + Note: Only trusted administrators should be allowed to edit the DevWorkspaceOperatorConfig. + items: + description: A single application container that you want to + run within a pod. + properties: + args: + description: |- + Arguments to the entrypoint. + The container image's CMD is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + description: |- + Entrypoint array. Not executed within a shell. + The container image's ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + description: |- + List of environment variables to set in the container. + Cannot be updated. + items: + description: EnvVar represents an environment variable + present in a Container. + properties: + name: + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's + value. Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + 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 + or its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in + the specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount + containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for + volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of + the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the + pod's namespace + properties: + key: + description: The key of the secret to select + from. Must be a valid secret key. + type: string + 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 or + its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + description: |- + List of sources to populate environment variables in the container. + The keys defined within a source may consist of any printable ASCII characters except '='. + When a key exists in multiple + sources, the value associated with the last source will take precedence. + Values defined by an Env with a duplicate key will take precedence. + Cannot be updated. + 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 + x-kubernetes-list-type: atomic + image: + description: |- + Container image name. + More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management to default or override + container images in workload controllers like Deployments and StatefulSets. + type: string + imagePullPolicy: + description: |- + Image pull policy. + One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/containers/images#updating-images + type: string + lifecycle: + description: |- + Actions that the management system should take in response to container lifecycle events. + Cannot be updated. + properties: + postStart: + description: |- + PostStart is called immediately after a container is created. If the handler fails, + the container is terminated and restarted according to its restart policy. + Other management of the container blocks until the hook completes. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks + properties: + exec: + description: Exec specifies a command to execute + in the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request + to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents a duration that the + container should sleep. + properties: + seconds: + description: Seconds is the number of seconds + to sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for backward compatibility. There is no validation of this field and + lifecycle hooks will fail at runtime when it is specified. + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: |- + PreStop is called immediately before a container is terminated due to an + API request or management event such as liveness/startup probe failure, + preemption, resource contention, etc. The handler is not called if the + container crashes or exits. The Pod's termination grace period countdown begins before the + PreStop hook is executed. Regardless of the outcome of the handler, the + container will eventually terminate within the Pod's termination grace + period (unless delayed by finalizers). Other management of the container blocks until the hook completes + or until the termination grace period is reached. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks + properties: + exec: + description: Exec specifies a command to execute + in the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request + to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents a duration that the + container should sleep. + properties: + seconds: + description: Seconds is the number of seconds + to sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for backward compatibility. There is no validation of this field and + lifecycle hooks will fail at runtime when it is specified. + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + description: |- + StopSignal defines which signal will be sent to a container when it is being stopped. + If not specified, the default is defined by the container runtime in use. + StopSignal can only be set for Pods with a non-empty .spec.os.name + type: string + type: object + livenessProbe: + description: |- + Periodic probe of container liveness. + Container will be restarted if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in + the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to + perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + name: + description: |- + Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: |- + List of ports to expose from the container. Not specifying a port here + DOES NOT prevent that port from being exposed. Any port which is + listening on the default "0.0.0.0" address inside a container will be + accessible from the network. + Modifying this array with strategic merge patch may corrupt the data. + For more information See https://github.com/kubernetes/kubernetes/issues/108255. + Cannot be updated. + items: + description: ContainerPort represents a network port in + a single container. + properties: + containerPort: + description: |- + Number of port to expose on the pod's IP address. + This must be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port + to. + type: string + hostPort: + description: |- + Number of port to expose on the host. + If specified, this must be a valid port number, 0 < x < 65536. + If HostNetwork is specified, this must match ContainerPort. + Most containers do not need this. + format: int32 + type: integer + name: + description: |- + If specified, this must be an IANA_SVC_NAME and unique within the pod. Each + named port in a pod must have a unique name. Name for the port that can be + referred to by services. + type: string + protocol: + default: TCP + description: |- + Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: |- + Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in + the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to + perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + resizePolicy: + description: Resources resize policy for the container. + items: + description: ContainerResizePolicy represents resource + resize policy for the container. + properties: + resourceName: + description: |- + Name of the resource to which this resource resize policy applies. + Supported values: cpu, memory. + type: string + restartPolicy: + description: |- + Restart policy to apply when specified resource is resized. + If not specified, it defaults to NotRequired. + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + description: |- + Compute Resources required by this container. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This field depends on the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in + PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + restartPolicy: + description: |- + RestartPolicy defines the restart behavior of individual containers in a pod. + This overrides the pod-level restart policy. When this field is not specified, + the restart behavior is defined by the Pod's restart policy and the container type. + Additionally, setting the RestartPolicy as "Always" for the init container will + have the following effect: + this init container will be continually restarted on + exit until all regular containers have terminated. Once all regular + containers have completed, all init containers with restartPolicy "Always" + will be shut down. This lifecycle differs from normal init containers and + is often referred to as a "sidecar" container. Although this init + container still starts in the init container sequence, it does not wait + for the container to complete before proceeding to the next init + container. Instead, the next init container starts immediately after this + init container is started, or after any startupProbe has successfully + completed. + type: string + restartPolicyRules: + description: |- + Represents a list of rules to be checked to determine if the + container should be restarted on exit. The rules are evaluated in + order. Once a rule matches a container exit condition, the remaining + rules are ignored. If no rule matches the container exit condition, + the Container-level restart policy determines the whether the container + is restarted or not. Constraints on the rules: + - At most 20 rules are allowed. + - Rules can have the same action. + - Identical rules are not forbidden in validations. + When rules are specified, container MUST set RestartPolicy explicitly + even it if matches the Pod's RestartPolicy. + items: + description: ContainerRestartRule describes how a container + exit is handled. + properties: + action: + description: |- + Specifies the action taken on a container exit if the requirements + are satisfied. The only possible value is "Restart" to restart the + container. + type: string + exitCodes: + description: Represents the exit codes to check on + container exits. + properties: + operator: + description: |- + Represents the relationship between the container exit code(s) and the + specified values. Possible values are: + - In: the requirement is satisfied if the container exit code is in the + set of specified values. + - NotIn: the requirement is satisfied if the container exit code is + not in the set of specified values. + type: string + values: + description: |- + Specifies the set of values to check for container exit codes. + At most 255 elements are allowed. + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic + securityContext: + description: |- + SecurityContext defines the security options the container should be run with. + If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: |- + procMount denotes the type of proc mount to use for the containers. + The default value is Default which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name + of the GMSA credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + startupProbe: + description: |- + StartupProbe indicates that the Pod has successfully initialized. + If specified, no other probes are executed until this completes successfully. + If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. + This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, + when it might take a long time to load data or warm a cache, than during steady-state operation. + This cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in + the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to + perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + stdin: + description: |- + Whether this container should allocate a buffer for stdin in the container runtime. If this + is not set, reads from stdin in the container will always result in EOF. + Default is false. + type: boolean + stdinOnce: + description: |- + Whether the container runtime should close the stdin channel after it has been opened by + a single attach. When stdin is true the stdin stream will remain open across multiple attach + sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the + first client attaches to stdin, and then remains open and accepts data until the client disconnects, + at which time stdin is closed and remains closed until the container is restarted. If this + flag is false, a container processes that reads from stdin will never receive an EOF. + Default is false + type: boolean + terminationMessagePath: + description: |- + Optional: Path at which the file to which the container's termination message + will be written is mounted into the container's filesystem. + Message written is intended to be brief final status, such as an assertion failure message. + Will be truncated by the node if greater than 4096 bytes. The total message length across + all containers will be limited to 12kb. + Defaults to /dev/termination-log. + Cannot be updated. + type: string + terminationMessagePolicy: + description: |- + Indicate how the termination message should be populated. File will use the contents of + terminationMessagePath to populate the container status message on both success and failure. + FallbackToLogsOnError will use the last chunk of container log output if the termination + message file is empty and the container exited with an error. + The log output is limited to 2048 bytes or 80 lines, whichever is smaller. + Defaults to File. + Cannot be updated. + type: string + tty: + description: |- + Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. + Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices + to be used by the container. + items: + description: volumeDevice describes a mapping of a raw + block device within a container. + properties: + devicePath: + description: devicePath is the path inside of the + container that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + description: |- + Pod volumes to mount into the container's filesystem. + Cannot be updated. + items: + description: VolumeMount describes a mounting of a Volume + within a container. + properties: + mountPath: + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: |- + mountPropagation determines how mounts are propagated from the host + to container and the other way around. + When not set, MountPropagationNone is used. + This field is beta in 1.10. + When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + (which defaults to None). + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: |- + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. + type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly specifies whether read-only mounts should be handled + recursively. + + If ReadOnly is false, this field has no meaning and must be unspecified. + + If ReadOnly is true, and this field is set to Disabled, the mount is not made + recursively read-only. If this field is set to IfPossible, the mount is made + recursively read-only, if it is supported by the container runtime. If this + field is set to Enabled, the mount is made recursively read-only if it is + supported by the container runtime, otherwise the pod will not be started and + an error will be generated to indicate the reason. + + If this field is set to IfPossible or Enabled, MountPropagation must be set to + None (or be unspecified, which defaults to None). + + If this field is not specified, it is treated as an equivalent of Disabled. + type: string + subPath: + description: |- + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + subPathExpr: + description: |- + Expanded path within the volume from which the container's volume should be mounted. + Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. + Defaults to "" (volume's root). + SubPathExpr and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + description: |- + Container's working directory. + If not specified, the container runtime's default will be used, which + might be configured in the container image. + Cannot be updated. + type: string + required: + - name + type: object + type: array persistUserHome: description: |- PersistUserHome defines configuration options for persisting the `/home/user/` diff --git a/deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml b/deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml index 910c7f6b5..34b9e2e78 100644 --- a/deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml +++ b/deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml @@ -2555,6 +2555,1539 @@ spec: - Always - Never type: string + initContainers: + description: |- + InitContainers are injected into all workspace pods as Kubernetes init containers. + Typical uses: injecting organization tools/configs, initializing persistent home, etc. + Note: Only trusted administrators should be allowed to edit the DevWorkspaceOperatorConfig. + items: + description: A single application container that you want to + run within a pod. + properties: + args: + description: |- + Arguments to the entrypoint. + The container image's CMD is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + description: |- + Entrypoint array. Not executed within a shell. + The container image's ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + description: |- + List of environment variables to set in the container. + Cannot be updated. + items: + description: EnvVar represents an environment variable + present in a Container. + properties: + name: + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's + value. Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + 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 + or its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in + the specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount + containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for + volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of + the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the + pod's namespace + properties: + key: + description: The key of the secret to select + from. Must be a valid secret key. + type: string + 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 or + its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + description: |- + List of sources to populate environment variables in the container. + The keys defined within a source may consist of any printable ASCII characters except '='. + When a key exists in multiple + sources, the value associated with the last source will take precedence. + Values defined by an Env with a duplicate key will take precedence. + Cannot be updated. + 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 + x-kubernetes-list-type: atomic + image: + description: |- + Container image name. + More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management to default or override + container images in workload controllers like Deployments and StatefulSets. + type: string + imagePullPolicy: + description: |- + Image pull policy. + One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/containers/images#updating-images + type: string + lifecycle: + description: |- + Actions that the management system should take in response to container lifecycle events. + Cannot be updated. + properties: + postStart: + description: |- + PostStart is called immediately after a container is created. If the handler fails, + the container is terminated and restarted according to its restart policy. + Other management of the container blocks until the hook completes. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks + properties: + exec: + description: Exec specifies a command to execute + in the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request + to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents a duration that the + container should sleep. + properties: + seconds: + description: Seconds is the number of seconds + to sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for backward compatibility. There is no validation of this field and + lifecycle hooks will fail at runtime when it is specified. + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: |- + PreStop is called immediately before a container is terminated due to an + API request or management event such as liveness/startup probe failure, + preemption, resource contention, etc. The handler is not called if the + container crashes or exits. The Pod's termination grace period countdown begins before the + PreStop hook is executed. Regardless of the outcome of the handler, the + container will eventually terminate within the Pod's termination grace + period (unless delayed by finalizers). Other management of the container blocks until the hook completes + or until the termination grace period is reached. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks + properties: + exec: + description: Exec specifies a command to execute + in the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request + to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents a duration that the + container should sleep. + properties: + seconds: + description: Seconds is the number of seconds + to sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for backward compatibility. There is no validation of this field and + lifecycle hooks will fail at runtime when it is specified. + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + description: |- + StopSignal defines which signal will be sent to a container when it is being stopped. + If not specified, the default is defined by the container runtime in use. + StopSignal can only be set for Pods with a non-empty .spec.os.name + type: string + type: object + livenessProbe: + description: |- + Periodic probe of container liveness. + Container will be restarted if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in + the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to + perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + name: + description: |- + Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: |- + List of ports to expose from the container. Not specifying a port here + DOES NOT prevent that port from being exposed. Any port which is + listening on the default "0.0.0.0" address inside a container will be + accessible from the network. + Modifying this array with strategic merge patch may corrupt the data. + For more information See https://github.com/kubernetes/kubernetes/issues/108255. + Cannot be updated. + items: + description: ContainerPort represents a network port in + a single container. + properties: + containerPort: + description: |- + Number of port to expose on the pod's IP address. + This must be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port + to. + type: string + hostPort: + description: |- + Number of port to expose on the host. + If specified, this must be a valid port number, 0 < x < 65536. + If HostNetwork is specified, this must match ContainerPort. + Most containers do not need this. + format: int32 + type: integer + name: + description: |- + If specified, this must be an IANA_SVC_NAME and unique within the pod. Each + named port in a pod must have a unique name. Name for the port that can be + referred to by services. + type: string + protocol: + default: TCP + description: |- + Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: |- + Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in + the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to + perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + resizePolicy: + description: Resources resize policy for the container. + items: + description: ContainerResizePolicy represents resource + resize policy for the container. + properties: + resourceName: + description: |- + Name of the resource to which this resource resize policy applies. + Supported values: cpu, memory. + type: string + restartPolicy: + description: |- + Restart policy to apply when specified resource is resized. + If not specified, it defaults to NotRequired. + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + description: |- + Compute Resources required by this container. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This field depends on the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in + PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + restartPolicy: + description: |- + RestartPolicy defines the restart behavior of individual containers in a pod. + This overrides the pod-level restart policy. When this field is not specified, + the restart behavior is defined by the Pod's restart policy and the container type. + Additionally, setting the RestartPolicy as "Always" for the init container will + have the following effect: + this init container will be continually restarted on + exit until all regular containers have terminated. Once all regular + containers have completed, all init containers with restartPolicy "Always" + will be shut down. This lifecycle differs from normal init containers and + is often referred to as a "sidecar" container. Although this init + container still starts in the init container sequence, it does not wait + for the container to complete before proceeding to the next init + container. Instead, the next init container starts immediately after this + init container is started, or after any startupProbe has successfully + completed. + type: string + restartPolicyRules: + description: |- + Represents a list of rules to be checked to determine if the + container should be restarted on exit. The rules are evaluated in + order. Once a rule matches a container exit condition, the remaining + rules are ignored. If no rule matches the container exit condition, + the Container-level restart policy determines the whether the container + is restarted or not. Constraints on the rules: + - At most 20 rules are allowed. + - Rules can have the same action. + - Identical rules are not forbidden in validations. + When rules are specified, container MUST set RestartPolicy explicitly + even it if matches the Pod's RestartPolicy. + items: + description: ContainerRestartRule describes how a container + exit is handled. + properties: + action: + description: |- + Specifies the action taken on a container exit if the requirements + are satisfied. The only possible value is "Restart" to restart the + container. + type: string + exitCodes: + description: Represents the exit codes to check on + container exits. + properties: + operator: + description: |- + Represents the relationship between the container exit code(s) and the + specified values. Possible values are: + - In: the requirement is satisfied if the container exit code is in the + set of specified values. + - NotIn: the requirement is satisfied if the container exit code is + not in the set of specified values. + type: string + values: + description: |- + Specifies the set of values to check for container exit codes. + At most 255 elements are allowed. + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic + securityContext: + description: |- + SecurityContext defines the security options the container should be run with. + If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: |- + procMount denotes the type of proc mount to use for the containers. + The default value is Default which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name + of the GMSA credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + startupProbe: + description: |- + StartupProbe indicates that the Pod has successfully initialized. + If specified, no other probes are executed until this completes successfully. + If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. + This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, + when it might take a long time to load data or warm a cache, than during steady-state operation. + This cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in + the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to + perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + stdin: + description: |- + Whether this container should allocate a buffer for stdin in the container runtime. If this + is not set, reads from stdin in the container will always result in EOF. + Default is false. + type: boolean + stdinOnce: + description: |- + Whether the container runtime should close the stdin channel after it has been opened by + a single attach. When stdin is true the stdin stream will remain open across multiple attach + sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the + first client attaches to stdin, and then remains open and accepts data until the client disconnects, + at which time stdin is closed and remains closed until the container is restarted. If this + flag is false, a container processes that reads from stdin will never receive an EOF. + Default is false + type: boolean + terminationMessagePath: + description: |- + Optional: Path at which the file to which the container's termination message + will be written is mounted into the container's filesystem. + Message written is intended to be brief final status, such as an assertion failure message. + Will be truncated by the node if greater than 4096 bytes. The total message length across + all containers will be limited to 12kb. + Defaults to /dev/termination-log. + Cannot be updated. + type: string + terminationMessagePolicy: + description: |- + Indicate how the termination message should be populated. File will use the contents of + terminationMessagePath to populate the container status message on both success and failure. + FallbackToLogsOnError will use the last chunk of container log output if the termination + message file is empty and the container exited with an error. + The log output is limited to 2048 bytes or 80 lines, whichever is smaller. + Defaults to File. + Cannot be updated. + type: string + tty: + description: |- + Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. + Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices + to be used by the container. + items: + description: volumeDevice describes a mapping of a raw + block device within a container. + properties: + devicePath: + description: devicePath is the path inside of the + container that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + description: |- + Pod volumes to mount into the container's filesystem. + Cannot be updated. + items: + description: VolumeMount describes a mounting of a Volume + within a container. + properties: + mountPath: + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: |- + mountPropagation determines how mounts are propagated from the host + to container and the other way around. + When not set, MountPropagationNone is used. + This field is beta in 1.10. + When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + (which defaults to None). + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: |- + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. + type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly specifies whether read-only mounts should be handled + recursively. + + If ReadOnly is false, this field has no meaning and must be unspecified. + + If ReadOnly is true, and this field is set to Disabled, the mount is not made + recursively read-only. If this field is set to IfPossible, the mount is made + recursively read-only, if it is supported by the container runtime. If this + field is set to Enabled, the mount is made recursively read-only if it is + supported by the container runtime, otherwise the pod will not be started and + an error will be generated to indicate the reason. + + If this field is set to IfPossible or Enabled, MountPropagation must be set to + None (or be unspecified, which defaults to None). + + If this field is not specified, it is treated as an equivalent of Disabled. + type: string + subPath: + description: |- + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + subPathExpr: + description: |- + Expanded path within the volume from which the container's volume should be mounted. + Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. + Defaults to "" (volume's root). + SubPathExpr and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + description: |- + Container's working directory. + If not specified, the container runtime's default will be used, which + might be configured in the container image. + Cannot be updated. + type: string + required: + - name + type: object + type: array persistUserHome: description: |- PersistUserHome defines configuration options for persisting the `/home/user/` diff --git a/deploy/deployment/openshift/combined.yaml b/deploy/deployment/openshift/combined.yaml index 31bb95d7f..60acc6c28 100644 --- a/deploy/deployment/openshift/combined.yaml +++ b/deploy/deployment/openshift/combined.yaml @@ -2555,6 +2555,1539 @@ spec: - Always - Never type: string + initContainers: + description: |- + InitContainers are injected into all workspace pods as Kubernetes init containers. + Typical uses: injecting organization tools/configs, initializing persistent home, etc. + Note: Only trusted administrators should be allowed to edit the DevWorkspaceOperatorConfig. + items: + description: A single application container that you want to + run within a pod. + properties: + args: + description: |- + Arguments to the entrypoint. + The container image's CMD is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + description: |- + Entrypoint array. Not executed within a shell. + The container image's ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + description: |- + List of environment variables to set in the container. + Cannot be updated. + items: + description: EnvVar represents an environment variable + present in a Container. + properties: + name: + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's + value. Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + 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 + or its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in + the specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount + containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for + volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of + the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the + pod's namespace + properties: + key: + description: The key of the secret to select + from. Must be a valid secret key. + type: string + 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 or + its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + description: |- + List of sources to populate environment variables in the container. + The keys defined within a source may consist of any printable ASCII characters except '='. + When a key exists in multiple + sources, the value associated with the last source will take precedence. + Values defined by an Env with a duplicate key will take precedence. + Cannot be updated. + 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 + x-kubernetes-list-type: atomic + image: + description: |- + Container image name. + More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management to default or override + container images in workload controllers like Deployments and StatefulSets. + type: string + imagePullPolicy: + description: |- + Image pull policy. + One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/containers/images#updating-images + type: string + lifecycle: + description: |- + Actions that the management system should take in response to container lifecycle events. + Cannot be updated. + properties: + postStart: + description: |- + PostStart is called immediately after a container is created. If the handler fails, + the container is terminated and restarted according to its restart policy. + Other management of the container blocks until the hook completes. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks + properties: + exec: + description: Exec specifies a command to execute + in the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request + to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents a duration that the + container should sleep. + properties: + seconds: + description: Seconds is the number of seconds + to sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for backward compatibility. There is no validation of this field and + lifecycle hooks will fail at runtime when it is specified. + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: |- + PreStop is called immediately before a container is terminated due to an + API request or management event such as liveness/startup probe failure, + preemption, resource contention, etc. The handler is not called if the + container crashes or exits. The Pod's termination grace period countdown begins before the + PreStop hook is executed. Regardless of the outcome of the handler, the + container will eventually terminate within the Pod's termination grace + period (unless delayed by finalizers). Other management of the container blocks until the hook completes + or until the termination grace period is reached. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks + properties: + exec: + description: Exec specifies a command to execute + in the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request + to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents a duration that the + container should sleep. + properties: + seconds: + description: Seconds is the number of seconds + to sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for backward compatibility. There is no validation of this field and + lifecycle hooks will fail at runtime when it is specified. + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + description: |- + StopSignal defines which signal will be sent to a container when it is being stopped. + If not specified, the default is defined by the container runtime in use. + StopSignal can only be set for Pods with a non-empty .spec.os.name + type: string + type: object + livenessProbe: + description: |- + Periodic probe of container liveness. + Container will be restarted if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in + the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to + perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + name: + description: |- + Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: |- + List of ports to expose from the container. Not specifying a port here + DOES NOT prevent that port from being exposed. Any port which is + listening on the default "0.0.0.0" address inside a container will be + accessible from the network. + Modifying this array with strategic merge patch may corrupt the data. + For more information See https://github.com/kubernetes/kubernetes/issues/108255. + Cannot be updated. + items: + description: ContainerPort represents a network port in + a single container. + properties: + containerPort: + description: |- + Number of port to expose on the pod's IP address. + This must be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port + to. + type: string + hostPort: + description: |- + Number of port to expose on the host. + If specified, this must be a valid port number, 0 < x < 65536. + If HostNetwork is specified, this must match ContainerPort. + Most containers do not need this. + format: int32 + type: integer + name: + description: |- + If specified, this must be an IANA_SVC_NAME and unique within the pod. Each + named port in a pod must have a unique name. Name for the port that can be + referred to by services. + type: string + protocol: + default: TCP + description: |- + Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: |- + Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in + the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to + perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + resizePolicy: + description: Resources resize policy for the container. + items: + description: ContainerResizePolicy represents resource + resize policy for the container. + properties: + resourceName: + description: |- + Name of the resource to which this resource resize policy applies. + Supported values: cpu, memory. + type: string + restartPolicy: + description: |- + Restart policy to apply when specified resource is resized. + If not specified, it defaults to NotRequired. + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + description: |- + Compute Resources required by this container. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This field depends on the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in + PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + restartPolicy: + description: |- + RestartPolicy defines the restart behavior of individual containers in a pod. + This overrides the pod-level restart policy. When this field is not specified, + the restart behavior is defined by the Pod's restart policy and the container type. + Additionally, setting the RestartPolicy as "Always" for the init container will + have the following effect: + this init container will be continually restarted on + exit until all regular containers have terminated. Once all regular + containers have completed, all init containers with restartPolicy "Always" + will be shut down. This lifecycle differs from normal init containers and + is often referred to as a "sidecar" container. Although this init + container still starts in the init container sequence, it does not wait + for the container to complete before proceeding to the next init + container. Instead, the next init container starts immediately after this + init container is started, or after any startupProbe has successfully + completed. + type: string + restartPolicyRules: + description: |- + Represents a list of rules to be checked to determine if the + container should be restarted on exit. The rules are evaluated in + order. Once a rule matches a container exit condition, the remaining + rules are ignored. If no rule matches the container exit condition, + the Container-level restart policy determines the whether the container + is restarted or not. Constraints on the rules: + - At most 20 rules are allowed. + - Rules can have the same action. + - Identical rules are not forbidden in validations. + When rules are specified, container MUST set RestartPolicy explicitly + even it if matches the Pod's RestartPolicy. + items: + description: ContainerRestartRule describes how a container + exit is handled. + properties: + action: + description: |- + Specifies the action taken on a container exit if the requirements + are satisfied. The only possible value is "Restart" to restart the + container. + type: string + exitCodes: + description: Represents the exit codes to check on + container exits. + properties: + operator: + description: |- + Represents the relationship between the container exit code(s) and the + specified values. Possible values are: + - In: the requirement is satisfied if the container exit code is in the + set of specified values. + - NotIn: the requirement is satisfied if the container exit code is + not in the set of specified values. + type: string + values: + description: |- + Specifies the set of values to check for container exit codes. + At most 255 elements are allowed. + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic + securityContext: + description: |- + SecurityContext defines the security options the container should be run with. + If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: |- + procMount denotes the type of proc mount to use for the containers. + The default value is Default which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name + of the GMSA credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + startupProbe: + description: |- + StartupProbe indicates that the Pod has successfully initialized. + If specified, no other probes are executed until this completes successfully. + If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. + This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, + when it might take a long time to load data or warm a cache, than during steady-state operation. + This cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in + the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to + perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + stdin: + description: |- + Whether this container should allocate a buffer for stdin in the container runtime. If this + is not set, reads from stdin in the container will always result in EOF. + Default is false. + type: boolean + stdinOnce: + description: |- + Whether the container runtime should close the stdin channel after it has been opened by + a single attach. When stdin is true the stdin stream will remain open across multiple attach + sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the + first client attaches to stdin, and then remains open and accepts data until the client disconnects, + at which time stdin is closed and remains closed until the container is restarted. If this + flag is false, a container processes that reads from stdin will never receive an EOF. + Default is false + type: boolean + terminationMessagePath: + description: |- + Optional: Path at which the file to which the container's termination message + will be written is mounted into the container's filesystem. + Message written is intended to be brief final status, such as an assertion failure message. + Will be truncated by the node if greater than 4096 bytes. The total message length across + all containers will be limited to 12kb. + Defaults to /dev/termination-log. + Cannot be updated. + type: string + terminationMessagePolicy: + description: |- + Indicate how the termination message should be populated. File will use the contents of + terminationMessagePath to populate the container status message on both success and failure. + FallbackToLogsOnError will use the last chunk of container log output if the termination + message file is empty and the container exited with an error. + The log output is limited to 2048 bytes or 80 lines, whichever is smaller. + Defaults to File. + Cannot be updated. + type: string + tty: + description: |- + Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. + Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices + to be used by the container. + items: + description: volumeDevice describes a mapping of a raw + block device within a container. + properties: + devicePath: + description: devicePath is the path inside of the + container that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + description: |- + Pod volumes to mount into the container's filesystem. + Cannot be updated. + items: + description: VolumeMount describes a mounting of a Volume + within a container. + properties: + mountPath: + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: |- + mountPropagation determines how mounts are propagated from the host + to container and the other way around. + When not set, MountPropagationNone is used. + This field is beta in 1.10. + When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + (which defaults to None). + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: |- + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. + type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly specifies whether read-only mounts should be handled + recursively. + + If ReadOnly is false, this field has no meaning and must be unspecified. + + If ReadOnly is true, and this field is set to Disabled, the mount is not made + recursively read-only. If this field is set to IfPossible, the mount is made + recursively read-only, if it is supported by the container runtime. If this + field is set to Enabled, the mount is made recursively read-only if it is + supported by the container runtime, otherwise the pod will not be started and + an error will be generated to indicate the reason. + + If this field is set to IfPossible or Enabled, MountPropagation must be set to + None (or be unspecified, which defaults to None). + + If this field is not specified, it is treated as an equivalent of Disabled. + type: string + subPath: + description: |- + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + subPathExpr: + description: |- + Expanded path within the volume from which the container's volume should be mounted. + Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. + Defaults to "" (volume's root). + SubPathExpr and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + description: |- + Container's working directory. + If not specified, the container runtime's default will be used, which + might be configured in the container image. + Cannot be updated. + type: string + required: + - name + type: object + type: array persistUserHome: description: |- PersistUserHome defines configuration options for persisting the `/home/user/` diff --git a/deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml b/deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml index 910c7f6b5..34b9e2e78 100644 --- a/deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml +++ b/deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml @@ -2555,6 +2555,1539 @@ spec: - Always - Never type: string + initContainers: + description: |- + InitContainers are injected into all workspace pods as Kubernetes init containers. + Typical uses: injecting organization tools/configs, initializing persistent home, etc. + Note: Only trusted administrators should be allowed to edit the DevWorkspaceOperatorConfig. + items: + description: A single application container that you want to + run within a pod. + properties: + args: + description: |- + Arguments to the entrypoint. + The container image's CMD is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + description: |- + Entrypoint array. Not executed within a shell. + The container image's ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + description: |- + List of environment variables to set in the container. + Cannot be updated. + items: + description: EnvVar represents an environment variable + present in a Container. + properties: + name: + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's + value. Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + 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 + or its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in + the specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount + containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for + volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of + the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the + pod's namespace + properties: + key: + description: The key of the secret to select + from. Must be a valid secret key. + type: string + 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 or + its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + description: |- + List of sources to populate environment variables in the container. + The keys defined within a source may consist of any printable ASCII characters except '='. + When a key exists in multiple + sources, the value associated with the last source will take precedence. + Values defined by an Env with a duplicate key will take precedence. + Cannot be updated. + 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 + x-kubernetes-list-type: atomic + image: + description: |- + Container image name. + More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management to default or override + container images in workload controllers like Deployments and StatefulSets. + type: string + imagePullPolicy: + description: |- + Image pull policy. + One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/containers/images#updating-images + type: string + lifecycle: + description: |- + Actions that the management system should take in response to container lifecycle events. + Cannot be updated. + properties: + postStart: + description: |- + PostStart is called immediately after a container is created. If the handler fails, + the container is terminated and restarted according to its restart policy. + Other management of the container blocks until the hook completes. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks + properties: + exec: + description: Exec specifies a command to execute + in the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request + to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents a duration that the + container should sleep. + properties: + seconds: + description: Seconds is the number of seconds + to sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for backward compatibility. There is no validation of this field and + lifecycle hooks will fail at runtime when it is specified. + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: |- + PreStop is called immediately before a container is terminated due to an + API request or management event such as liveness/startup probe failure, + preemption, resource contention, etc. The handler is not called if the + container crashes or exits. The Pod's termination grace period countdown begins before the + PreStop hook is executed. Regardless of the outcome of the handler, the + container will eventually terminate within the Pod's termination grace + period (unless delayed by finalizers). Other management of the container blocks until the hook completes + or until the termination grace period is reached. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks + properties: + exec: + description: Exec specifies a command to execute + in the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request + to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents a duration that the + container should sleep. + properties: + seconds: + description: Seconds is the number of seconds + to sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for backward compatibility. There is no validation of this field and + lifecycle hooks will fail at runtime when it is specified. + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + description: |- + StopSignal defines which signal will be sent to a container when it is being stopped. + If not specified, the default is defined by the container runtime in use. + StopSignal can only be set for Pods with a non-empty .spec.os.name + type: string + type: object + livenessProbe: + description: |- + Periodic probe of container liveness. + Container will be restarted if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in + the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to + perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + name: + description: |- + Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: |- + List of ports to expose from the container. Not specifying a port here + DOES NOT prevent that port from being exposed. Any port which is + listening on the default "0.0.0.0" address inside a container will be + accessible from the network. + Modifying this array with strategic merge patch may corrupt the data. + For more information See https://github.com/kubernetes/kubernetes/issues/108255. + Cannot be updated. + items: + description: ContainerPort represents a network port in + a single container. + properties: + containerPort: + description: |- + Number of port to expose on the pod's IP address. + This must be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port + to. + type: string + hostPort: + description: |- + Number of port to expose on the host. + If specified, this must be a valid port number, 0 < x < 65536. + If HostNetwork is specified, this must match ContainerPort. + Most containers do not need this. + format: int32 + type: integer + name: + description: |- + If specified, this must be an IANA_SVC_NAME and unique within the pod. Each + named port in a pod must have a unique name. Name for the port that can be + referred to by services. + type: string + protocol: + default: TCP + description: |- + Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: |- + Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in + the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to + perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + resizePolicy: + description: Resources resize policy for the container. + items: + description: ContainerResizePolicy represents resource + resize policy for the container. + properties: + resourceName: + description: |- + Name of the resource to which this resource resize policy applies. + Supported values: cpu, memory. + type: string + restartPolicy: + description: |- + Restart policy to apply when specified resource is resized. + If not specified, it defaults to NotRequired. + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + description: |- + Compute Resources required by this container. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This field depends on the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in + PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + restartPolicy: + description: |- + RestartPolicy defines the restart behavior of individual containers in a pod. + This overrides the pod-level restart policy. When this field is not specified, + the restart behavior is defined by the Pod's restart policy and the container type. + Additionally, setting the RestartPolicy as "Always" for the init container will + have the following effect: + this init container will be continually restarted on + exit until all regular containers have terminated. Once all regular + containers have completed, all init containers with restartPolicy "Always" + will be shut down. This lifecycle differs from normal init containers and + is often referred to as a "sidecar" container. Although this init + container still starts in the init container sequence, it does not wait + for the container to complete before proceeding to the next init + container. Instead, the next init container starts immediately after this + init container is started, or after any startupProbe has successfully + completed. + type: string + restartPolicyRules: + description: |- + Represents a list of rules to be checked to determine if the + container should be restarted on exit. The rules are evaluated in + order. Once a rule matches a container exit condition, the remaining + rules are ignored. If no rule matches the container exit condition, + the Container-level restart policy determines the whether the container + is restarted or not. Constraints on the rules: + - At most 20 rules are allowed. + - Rules can have the same action. + - Identical rules are not forbidden in validations. + When rules are specified, container MUST set RestartPolicy explicitly + even it if matches the Pod's RestartPolicy. + items: + description: ContainerRestartRule describes how a container + exit is handled. + properties: + action: + description: |- + Specifies the action taken on a container exit if the requirements + are satisfied. The only possible value is "Restart" to restart the + container. + type: string + exitCodes: + description: Represents the exit codes to check on + container exits. + properties: + operator: + description: |- + Represents the relationship between the container exit code(s) and the + specified values. Possible values are: + - In: the requirement is satisfied if the container exit code is in the + set of specified values. + - NotIn: the requirement is satisfied if the container exit code is + not in the set of specified values. + type: string + values: + description: |- + Specifies the set of values to check for container exit codes. + At most 255 elements are allowed. + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic + securityContext: + description: |- + SecurityContext defines the security options the container should be run with. + If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: |- + procMount denotes the type of proc mount to use for the containers. + The default value is Default which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name + of the GMSA credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + startupProbe: + description: |- + StartupProbe indicates that the Pod has successfully initialized. + If specified, no other probes are executed until this completes successfully. + If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. + This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, + when it might take a long time to load data or warm a cache, than during steady-state operation. + This cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in + the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to + perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + stdin: + description: |- + Whether this container should allocate a buffer for stdin in the container runtime. If this + is not set, reads from stdin in the container will always result in EOF. + Default is false. + type: boolean + stdinOnce: + description: |- + Whether the container runtime should close the stdin channel after it has been opened by + a single attach. When stdin is true the stdin stream will remain open across multiple attach + sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the + first client attaches to stdin, and then remains open and accepts data until the client disconnects, + at which time stdin is closed and remains closed until the container is restarted. If this + flag is false, a container processes that reads from stdin will never receive an EOF. + Default is false + type: boolean + terminationMessagePath: + description: |- + Optional: Path at which the file to which the container's termination message + will be written is mounted into the container's filesystem. + Message written is intended to be brief final status, such as an assertion failure message. + Will be truncated by the node if greater than 4096 bytes. The total message length across + all containers will be limited to 12kb. + Defaults to /dev/termination-log. + Cannot be updated. + type: string + terminationMessagePolicy: + description: |- + Indicate how the termination message should be populated. File will use the contents of + terminationMessagePath to populate the container status message on both success and failure. + FallbackToLogsOnError will use the last chunk of container log output if the termination + message file is empty and the container exited with an error. + The log output is limited to 2048 bytes or 80 lines, whichever is smaller. + Defaults to File. + Cannot be updated. + type: string + tty: + description: |- + Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. + Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices + to be used by the container. + items: + description: volumeDevice describes a mapping of a raw + block device within a container. + properties: + devicePath: + description: devicePath is the path inside of the + container that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + description: |- + Pod volumes to mount into the container's filesystem. + Cannot be updated. + items: + description: VolumeMount describes a mounting of a Volume + within a container. + properties: + mountPath: + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: |- + mountPropagation determines how mounts are propagated from the host + to container and the other way around. + When not set, MountPropagationNone is used. + This field is beta in 1.10. + When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + (which defaults to None). + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: |- + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. + type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly specifies whether read-only mounts should be handled + recursively. + + If ReadOnly is false, this field has no meaning and must be unspecified. + + If ReadOnly is true, and this field is set to Disabled, the mount is not made + recursively read-only. If this field is set to IfPossible, the mount is made + recursively read-only, if it is supported by the container runtime. If this + field is set to Enabled, the mount is made recursively read-only if it is + supported by the container runtime, otherwise the pod will not be started and + an error will be generated to indicate the reason. + + If this field is set to IfPossible or Enabled, MountPropagation must be set to + None (or be unspecified, which defaults to None). + + If this field is not specified, it is treated as an equivalent of Disabled. + type: string + subPath: + description: |- + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + subPathExpr: + description: |- + Expanded path within the volume from which the container's volume should be mounted. + Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. + Defaults to "" (volume's root). + SubPathExpr and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + description: |- + Container's working directory. + If not specified, the container runtime's default will be used, which + might be configured in the container image. + Cannot be updated. + type: string + required: + - name + type: object + type: array persistUserHome: description: |- PersistUserHome defines configuration options for persisting the `/home/user/` diff --git a/deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml b/deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml index d03cd57bd..7fe958169 100644 --- a/deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml +++ b/deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml @@ -2553,6 +2553,1539 @@ spec: - Always - Never type: string + initContainers: + description: |- + InitContainers are injected into all workspace pods as Kubernetes init containers. + Typical uses: injecting organization tools/configs, initializing persistent home, etc. + Note: Only trusted administrators should be allowed to edit the DevWorkspaceOperatorConfig. + items: + description: A single application container that you want to + run within a pod. + properties: + args: + description: |- + Arguments to the entrypoint. + The container image's CMD is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + items: + type: string + type: array + x-kubernetes-list-type: atomic + command: + description: |- + Entrypoint array. Not executed within a shell. + The container image's ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + items: + type: string + type: array + x-kubernetes-list-type: atomic + env: + description: |- + List of environment variables to set in the container. + Cannot be updated. + items: + description: EnvVar represents an environment variable + present in a Container. + properties: + name: + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's + value. Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + 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 + or its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in + the specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount + containing the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for + volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of + the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the + pod's namespace + properties: + key: + description: The key of the secret to select + from. Must be a valid secret key. + type: string + 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 or + its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + description: |- + List of sources to populate environment variables in the container. + The keys defined within a source may consist of any printable ASCII characters except '='. + When a key exists in multiple + sources, the value associated with the last source will take precedence. + Values defined by an Env with a duplicate key will take precedence. + Cannot be updated. + 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 + x-kubernetes-list-type: atomic + image: + description: |- + Container image name. + More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management to default or override + container images in workload controllers like Deployments and StatefulSets. + type: string + imagePullPolicy: + description: |- + Image pull policy. + One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/containers/images#updating-images + type: string + lifecycle: + description: |- + Actions that the management system should take in response to container lifecycle events. + Cannot be updated. + properties: + postStart: + description: |- + PostStart is called immediately after a container is created. If the handler fails, + the container is terminated and restarted according to its restart policy. + Other management of the container blocks until the hook completes. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks + properties: + exec: + description: Exec specifies a command to execute + in the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request + to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents a duration that the + container should sleep. + properties: + seconds: + description: Seconds is the number of seconds + to sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for backward compatibility. There is no validation of this field and + lifecycle hooks will fail at runtime when it is specified. + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: |- + PreStop is called immediately before a container is terminated due to an + API request or management event such as liveness/startup probe failure, + preemption, resource contention, etc. The handler is not called if the + container crashes or exits. The Pod's termination grace period countdown begins before the + PreStop hook is executed. Regardless of the outcome of the handler, the + container will eventually terminate within the Pod's termination grace + period (unless delayed by finalizers). Other management of the container blocks until the hook completes + or until the termination grace period is reached. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks + properties: + exec: + description: Exec specifies a command to execute + in the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request + to perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + sleep: + description: Sleep represents a duration that the + container should sleep. + properties: + seconds: + description: Seconds is the number of seconds + to sleep. + format: int64 + type: integer + required: + - seconds + type: object + tcpSocket: + description: |- + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept + for backward compatibility. There is no validation of this field and + lifecycle hooks will fail at runtime when it is specified. + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + stopSignal: + description: |- + StopSignal defines which signal will be sent to a container when it is being stopped. + If not specified, the default is defined by the container runtime in use. + StopSignal can only be set for Pods with a non-empty .spec.os.name + type: string + type: object + livenessProbe: + description: |- + Periodic probe of container liveness. + Container will be restarted if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in + the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to + perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + name: + description: |- + Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: |- + List of ports to expose from the container. Not specifying a port here + DOES NOT prevent that port from being exposed. Any port which is + listening on the default "0.0.0.0" address inside a container will be + accessible from the network. + Modifying this array with strategic merge patch may corrupt the data. + For more information See https://github.com/kubernetes/kubernetes/issues/108255. + Cannot be updated. + items: + description: ContainerPort represents a network port in + a single container. + properties: + containerPort: + description: |- + Number of port to expose on the pod's IP address. + This must be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port + to. + type: string + hostPort: + description: |- + Number of port to expose on the host. + If specified, this must be a valid port number, 0 < x < 65536. + If HostNetwork is specified, this must match ContainerPort. + Most containers do not need this. + format: int32 + type: integer + name: + description: |- + If specified, this must be an IANA_SVC_NAME and unique within the pod. Each + named port in a pod must have a unique name. Name for the port that can be + referred to by services. + type: string + protocol: + default: TCP + description: |- + Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: |- + Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in + the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to + perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + resizePolicy: + description: Resources resize policy for the container. + items: + description: ContainerResizePolicy represents resource + resize policy for the container. + properties: + resourceName: + description: |- + Name of the resource to which this resource resize policy applies. + Supported values: cpu, memory. + type: string + restartPolicy: + description: |- + Restart policy to apply when specified resource is resized. + If not specified, it defaults to NotRequired. + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + description: |- + Compute Resources required by this container. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This field depends on the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in + PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + restartPolicy: + description: |- + RestartPolicy defines the restart behavior of individual containers in a pod. + This overrides the pod-level restart policy. When this field is not specified, + the restart behavior is defined by the Pod's restart policy and the container type. + Additionally, setting the RestartPolicy as "Always" for the init container will + have the following effect: + this init container will be continually restarted on + exit until all regular containers have terminated. Once all regular + containers have completed, all init containers with restartPolicy "Always" + will be shut down. This lifecycle differs from normal init containers and + is often referred to as a "sidecar" container. Although this init + container still starts in the init container sequence, it does not wait + for the container to complete before proceeding to the next init + container. Instead, the next init container starts immediately after this + init container is started, or after any startupProbe has successfully + completed. + type: string + restartPolicyRules: + description: |- + Represents a list of rules to be checked to determine if the + container should be restarted on exit. The rules are evaluated in + order. Once a rule matches a container exit condition, the remaining + rules are ignored. If no rule matches the container exit condition, + the Container-level restart policy determines the whether the container + is restarted or not. Constraints on the rules: + - At most 20 rules are allowed. + - Rules can have the same action. + - Identical rules are not forbidden in validations. + When rules are specified, container MUST set RestartPolicy explicitly + even it if matches the Pod's RestartPolicy. + items: + description: ContainerRestartRule describes how a container + exit is handled. + properties: + action: + description: |- + Specifies the action taken on a container exit if the requirements + are satisfied. The only possible value is "Restart" to restart the + container. + type: string + exitCodes: + description: Represents the exit codes to check on + container exits. + properties: + operator: + description: |- + Represents the relationship between the container exit code(s) and the + specified values. Possible values are: + - In: the requirement is satisfied if the container exit code is in the + set of specified values. + - NotIn: the requirement is satisfied if the container exit code is + not in the set of specified values. + type: string + values: + description: |- + Specifies the set of values to check for container exit codes. + At most 255 elements are allowed. + items: + format: int32 + type: integer + type: array + x-kubernetes-list-type: set + required: + - operator + type: object + required: + - action + type: object + type: array + x-kubernetes-list-type: atomic + securityContext: + description: |- + SecurityContext defines the security options the container should be run with. + If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: |- + procMount denotes the type of proc mount to use for the containers. + The default value is Default which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name + of the GMSA credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + startupProbe: + description: |- + StartupProbe indicates that the Pod has successfully initialized. + If specified, no other probes are executed until this completes successfully. + If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. + This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, + when it might take a long time to load data or warm a cache, than during steady-state operation. + This cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + properties: + exec: + description: Exec specifies a command to execute in + the container. + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + x-kubernetes-list-type: atomic + type: object + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + default: "" + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies an HTTP GET request to + perform. + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies a connection to a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: |- + Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + The grace period is the duration in seconds after the processes running in the pod are sent + a termination signal and the time when the processes are forcibly halted with a kill signal. + Set this value longer than the expected cleanup time for your process. + If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. + Value must be non-negative integer. The value zero indicates stop immediately via + the kill signal (no opportunity to shut down). + This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + format: int32 + type: integer + type: object + stdin: + description: |- + Whether this container should allocate a buffer for stdin in the container runtime. If this + is not set, reads from stdin in the container will always result in EOF. + Default is false. + type: boolean + stdinOnce: + description: |- + Whether the container runtime should close the stdin channel after it has been opened by + a single attach. When stdin is true the stdin stream will remain open across multiple attach + sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the + first client attaches to stdin, and then remains open and accepts data until the client disconnects, + at which time stdin is closed and remains closed until the container is restarted. If this + flag is false, a container processes that reads from stdin will never receive an EOF. + Default is false + type: boolean + terminationMessagePath: + description: |- + Optional: Path at which the file to which the container's termination message + will be written is mounted into the container's filesystem. + Message written is intended to be brief final status, such as an assertion failure message. + Will be truncated by the node if greater than 4096 bytes. The total message length across + all containers will be limited to 12kb. + Defaults to /dev/termination-log. + Cannot be updated. + type: string + terminationMessagePolicy: + description: |- + Indicate how the termination message should be populated. File will use the contents of + terminationMessagePath to populate the container status message on both success and failure. + FallbackToLogsOnError will use the last chunk of container log output if the termination + message file is empty and the container exited with an error. + The log output is limited to 2048 bytes or 80 lines, whichever is smaller. + Defaults to File. + Cannot be updated. + type: string + tty: + description: |- + Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. + Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices + to be used by the container. + items: + description: volumeDevice describes a mapping of a raw + block device within a container. + properties: + devicePath: + description: devicePath is the path inside of the + container that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - devicePath + x-kubernetes-list-type: map + volumeMounts: + description: |- + Pod volumes to mount into the container's filesystem. + Cannot be updated. + items: + description: VolumeMount describes a mounting of a Volume + within a container. + properties: + mountPath: + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: |- + mountPropagation determines how mounts are propagated from the host + to container and the other way around. + When not set, MountPropagationNone is used. + This field is beta in 1.10. + When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + (which defaults to None). + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: |- + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. + type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly specifies whether read-only mounts should be handled + recursively. + + If ReadOnly is false, this field has no meaning and must be unspecified. + + If ReadOnly is true, and this field is set to Disabled, the mount is not made + recursively read-only. If this field is set to IfPossible, the mount is made + recursively read-only, if it is supported by the container runtime. If this + field is set to Enabled, the mount is made recursively read-only if it is + supported by the container runtime, otherwise the pod will not be started and + an error will be generated to indicate the reason. + + If this field is set to IfPossible or Enabled, MountPropagation must be set to + None (or be unspecified, which defaults to None). + + If this field is not specified, it is treated as an equivalent of Disabled. + type: string + subPath: + description: |- + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + subPathExpr: + description: |- + Expanded path within the volume from which the container's volume should be mounted. + Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. + Defaults to "" (volume's root). + SubPathExpr and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + description: |- + Container's working directory. + If not specified, the container runtime's default will be used, which + might be configured in the container image. + Cannot be updated. + type: string + required: + - name + type: object + type: array persistUserHome: description: |- PersistUserHome defines configuration options for persisting the `/home/user/` From e566270deeab31ca5a81bef704a925ea858bca54 Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Thu, 30 Oct 2025 15:55:15 +0200 Subject: [PATCH 03/30] test: unit and e2e tests for init containers Signed-off-by: Oleksii Kurinnyi --- .../init_container_validation_test.go | 303 +++++++++++++++ pkg/config/sync_test.go | 3 +- pkg/library/home/custom_init_test.go | 348 ++++++++++++++++++ test/e2e/pkg/client/oc.go | 10 + .../pkg/tests/custom_init_container_tests.go | 156 ++++++++ .../resources/custom-init-test-workspace.yaml | 16 + .../disabled-init-test-workspace.yaml | 16 + test/resources/dwoc-custom-init.yaml | 15 + test/resources/dwoc-disabled-init.yaml | 10 + 9 files changed, 876 insertions(+), 1 deletion(-) create mode 100644 controllers/workspace/init_container_validation_test.go create mode 100644 pkg/library/home/custom_init_test.go create mode 100644 test/e2e/pkg/tests/custom_init_container_tests.go create mode 100644 test/resources/custom-init-test-workspace.yaml create mode 100644 test/resources/disabled-init-test-workspace.yaml create mode 100644 test/resources/dwoc-custom-init.yaml create mode 100644 test/resources/dwoc-disabled-init.yaml diff --git a/controllers/workspace/init_container_validation_test.go b/controllers/workspace/init_container_validation_test.go new file mode 100644 index 000000000..dbfd290cd --- /dev/null +++ b/controllers/workspace/init_container_validation_test.go @@ -0,0 +1,303 @@ +// +// Copyright (c) 2019-2025 Red Hat, Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package controllers + +import ( + "testing" + + dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" + "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1" + "github.com/devfile/devworkspace-operator/pkg/common" + "github.com/devfile/devworkspace-operator/pkg/constants" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" +) + +func TestDefaultAndValidateHomeInitContainer(t *testing.T) { + workspace := &common.DevWorkspaceWithConfig{ + DevWorkspace: &dw.DevWorkspace{ + Spec: dw.DevWorkspaceSpec{ + Template: dw.DevWorkspaceTemplateSpec{ + DevWorkspaceTemplateSpecContent: dw.DevWorkspaceTemplateSpecContent{ + Components: []dw.Component{ + { + Name: "main-container", + ComponentUnion: dw.ComponentUnion{ + Container: &dw.ContainerComponent{ + Container: dw.Container{ + Image: "test-image:latest", + }, + }, + }, + }, + }, + }, + }, + }, + }, + Config: &v1alpha1.OperatorConfiguration{ + Workspace: &v1alpha1.WorkspaceConfig{}, + }, + } + + tests := []struct { + name string + container corev1.Container + expectError bool + errorMsg string + validate func(t *testing.T, result corev1.Container) + }{ + { + name: "Defaults image when empty", + container: corev1.Container{ + Name: constants.HomeInitComponentName, + Args: []string{"echo 'test'"}, + }, + expectError: false, + validate: func(t *testing.T, result corev1.Container) { + assert.Equal(t, "test-image:latest", result.Image) + }, + }, + { + name: "Defaults command when empty", + container: corev1.Container{ + Name: constants.HomeInitComponentName, + Image: "custom-image:latest", + Args: []string{"echo 'test'"}, + }, + expectError: false, + validate: func(t *testing.T, result corev1.Container) { + assert.Equal(t, []string{"/bin/sh", "-c"}, result.Command) + }, + }, + { + name: "Accepts valid command", + container: corev1.Container{ + Name: constants.HomeInitComponentName, + Image: "custom-image:latest", + Command: []string{"/bin/sh", "-c"}, + Args: []string{"echo 'test'"}, + }, + expectError: false, + }, + { + name: "Rejects invalid command", + container: corev1.Container{ + Name: constants.HomeInitComponentName, + Image: "custom-image:latest", + Command: []string{"/bin/bash"}, + Args: []string{"echo 'test'"}, + }, + expectError: true, + errorMsg: "command must be exactly [/bin/sh, -c]", + }, + { + name: "Rejects empty args", + container: corev1.Container{ + Name: constants.HomeInitComponentName, + Image: "custom-image:latest", + Args: []string{}, + }, + expectError: true, + errorMsg: "args must contain exactly one script string", + }, + { + name: "Rejects multiple args", + container: corev1.Container{ + Name: constants.HomeInitComponentName, + Image: "custom-image:latest", + Args: []string{"echo 'test'", "echo 'test2'"}, + }, + expectError: true, + errorMsg: "args must contain exactly one script string", + }, + { + name: "Rejects user-provided volumeMounts", + container: corev1.Container{ + Name: constants.HomeInitComponentName, + Image: "custom-image:latest", + Args: []string{"echo 'test'"}, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "custom-volume", + MountPath: "/mnt/custom", + }, + }, + }, + expectError: true, + errorMsg: "volumeMounts are not allowed for init-persistent-home", + }, + { + name: "Injects persistent-home volumeMount", + container: corev1.Container{ + Name: constants.HomeInitComponentName, + Image: "custom-image:latest", + Args: []string{"echo 'test'"}, + }, + expectError: false, + validate: func(t *testing.T, result corev1.Container) { + assert.Len(t, result.VolumeMounts, 1) + assert.Equal(t, constants.HomeVolumeName, result.VolumeMounts[0].Name) + assert.Equal(t, constants.HomeUserDirectory, result.VolumeMounts[0].MountPath) + }, + }, + { + name: "Allows env variables", + container: corev1.Container{ + Name: constants.HomeInitComponentName, + Image: "custom-image:latest", + Args: []string{"echo 'test'"}, + Env: []corev1.EnvVar{ + {Name: "TEST_VAR", Value: "test-value"}, + }, + }, + expectError: false, + validate: func(t *testing.T, result corev1.Container) { + assert.Len(t, result.Env, 1) + assert.Equal(t, "TEST_VAR", result.Env[0].Name) + }, + }, + { + name: "Rejects ports", + container: corev1.Container{ + Name: constants.HomeInitComponentName, + Image: "custom-image:latest", + Args: []string{"echo 'test'"}, + Ports: []corev1.ContainerPort{ + {ContainerPort: 8080}, + }, + }, + expectError: true, + errorMsg: "ports are not allowed", + }, + { + name: "Rejects livenessProbe", + container: corev1.Container{ + Name: constants.HomeInitComponentName, + Image: "custom-image:latest", + Args: []string{"echo 'test'"}, + LivenessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{Path: "/health"}, + }, + }, + }, + expectError: true, + errorMsg: "probes are not allowed", + }, + { + name: "Rejects securityContext", + container: corev1.Container{ + Name: constants.HomeInitComponentName, + Image: "custom-image:latest", + Args: []string{"echo 'test'"}, + SecurityContext: &corev1.SecurityContext{ + RunAsUser: new(int64), + }, + }, + expectError: true, + errorMsg: "securityContext is not allowed", + }, + { + name: "Rejects resources", + container: corev1.Container{ + Name: constants.HomeInitComponentName, + Image: "custom-image:latest", + Args: []string{"echo 'test'"}, + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("128Mi"), + }, + }, + }, + expectError: true, + errorMsg: "resource limits/requests are not allowed", + }, + { + name: "Rejects workingDir", + container: corev1.Container{ + Name: constants.HomeInitComponentName, + Image: "custom-image:latest", + Args: []string{"echo 'test'"}, + WorkingDir: "/tmp", + }, + expectError: true, + errorMsg: "volumeDevices and workingDir are not allowed", + }, + { + name: "Rejects image with whitespace", + container: corev1.Container{ + Name: constants.HomeInitComponentName, + Image: "nginx\nmalicious", + Args: []string{"echo 'test'"}, + }, + expectError: true, + errorMsg: "invalid image reference", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := defaultAndValidateHomeInitContainer(tt.container, workspace) + + if tt.expectError { + assert.Error(t, err) + if tt.errorMsg != "" { + assert.Contains(t, err.Error(), tt.errorMsg) + } + } else { + assert.NoError(t, err) + if tt.validate != nil { + tt.validate(t, result) + } + } + }) + } +} + +func TestDefaultAndValidateHomeInitContainer_NoWorkspaceImage(t *testing.T) { + workspaceNoImage := &common.DevWorkspaceWithConfig{ + DevWorkspace: &dw.DevWorkspace{ + Spec: dw.DevWorkspaceSpec{ + Template: dw.DevWorkspaceTemplateSpec{ + DevWorkspaceTemplateSpecContent: dw.DevWorkspaceTemplateSpecContent{ + Components: []dw.Component{ + { + Name: "volume-component", + ComponentUnion: dw.ComponentUnion{ + Volume: &dw.VolumeComponent{}, + }, + }, + }, + }, + }, + }, + }, + Config: &v1alpha1.OperatorConfiguration{ + Workspace: &v1alpha1.WorkspaceConfig{}, + }, + } + + container := corev1.Container{ + Name: constants.HomeInitComponentName, + Args: []string{"echo 'test'"}, + } + + _, err := defaultAndValidateHomeInitContainer(container, workspaceNoImage) + assert.Error(t, err) + assert.Contains(t, err.Error(), "unable to infer workspace image") +} diff --git a/pkg/config/sync_test.go b/pkg/config/sync_test.go index f6443fb17..c01cada4a 100644 --- a/pkg/config/sync_test.go +++ b/pkg/config/sync_test.go @@ -114,9 +114,10 @@ func TestMergesAllFieldsFromClusterConfig(t *testing.T) { for i := 0; i < 100; i++ { fuzzedConfig := &v1alpha1.OperatorConfiguration{} f.Fuzz(fuzzedConfig) - // Skip checking these two fields as they're interface fields and hard to fuzz. + // Skip checking these fields as they're interface fields and hard to fuzz. fuzzedConfig.Workspace.DefaultStorageSize = defaultConfig.Workspace.DefaultStorageSize.DeepCopy() fuzzedConfig.Workspace.PodSecurityContext = defaultConfig.Workspace.PodSecurityContext.DeepCopy() + fuzzedConfig.Workspace.InitContainers = defaultConfig.Workspace.InitContainers clusterConfig := buildConfig(fuzzedConfig) client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(clusterConfig).Build() err := SetupControllerConfig(client) diff --git a/pkg/library/home/custom_init_test.go b/pkg/library/home/custom_init_test.go new file mode 100644 index 000000000..6ecb0f34f --- /dev/null +++ b/pkg/library/home/custom_init_test.go @@ -0,0 +1,348 @@ +// +// Copyright (c) 2019-2025 Red Hat, Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package home + +import ( + "testing" + + dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" + attributes "github.com/devfile/api/v2/pkg/attributes" + "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1" + "github.com/devfile/devworkspace-operator/pkg/common" + "github.com/devfile/devworkspace-operator/pkg/constants" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + "k8s.io/utils/pointer" +) + +func TestCustomInitPersistentHome(t *testing.T) { + tests := []struct { + name string + workspace *common.DevWorkspaceWithConfig + expectDefaultInitAdded bool + expectCustomInitSkipped bool + }{ + { + name: "Skips default init when custom init-persistent-home is provided", + workspace: &common.DevWorkspaceWithConfig{ + DevWorkspace: &dw.DevWorkspace{ + Spec: dw.DevWorkspaceSpec{ + Template: dw.DevWorkspaceTemplateSpec{ + DevWorkspaceTemplateSpecContent: dw.DevWorkspaceTemplateSpecContent{ + Components: []dw.Component{ + { + Name: "test-container", + ComponentUnion: dw.ComponentUnion{ + Container: &dw.ContainerComponent{ + Container: dw.Container{ + Image: "test-image:latest", + }, + }, + }, + }, + }, + }, + }, + }, + }, + Config: &v1alpha1.OperatorConfiguration{ + Workspace: &v1alpha1.WorkspaceConfig{ + PersistUserHome: &v1alpha1.PersistentHomeConfig{ + Enabled: pointer.Bool(true), + }, + InitContainers: []corev1.Container{ + { + Name: constants.HomeInitComponentName, + Args: []string{"echo 'custom init'"}, + }, + }, + }, + }, + }, + expectDefaultInitAdded: false, + }, + { + name: "Adds default init when no custom init-persistent-home is provided", + workspace: &common.DevWorkspaceWithConfig{ + DevWorkspace: &dw.DevWorkspace{ + Spec: dw.DevWorkspaceSpec{ + Template: dw.DevWorkspaceTemplateSpec{ + DevWorkspaceTemplateSpecContent: dw.DevWorkspaceTemplateSpecContent{ + Components: []dw.Component{ + { + Name: "test-container", + ComponentUnion: dw.ComponentUnion{ + Container: &dw.ContainerComponent{ + Container: dw.Container{ + Image: "test-image:latest", + }, + }, + }, + }, + }, + }, + }, + }, + }, + Config: &v1alpha1.OperatorConfiguration{ + Workspace: &v1alpha1.WorkspaceConfig{ + PersistUserHome: &v1alpha1.PersistentHomeConfig{ + Enabled: pointer.Bool(true), + }, + InitContainers: []corev1.Container{ + { + Name: "custom-container", + Image: "custom:latest", + Args: []string{"echo 'other init'"}, + }, + }, + }, + }, + }, + expectDefaultInitAdded: true, + }, + { + name: "Adds default init when custom init containers list is empty", + workspace: &common.DevWorkspaceWithConfig{ + DevWorkspace: &dw.DevWorkspace{ + Spec: dw.DevWorkspaceSpec{ + Template: dw.DevWorkspaceTemplateSpec{ + DevWorkspaceTemplateSpecContent: dw.DevWorkspaceTemplateSpecContent{ + Components: []dw.Component{ + { + Name: "test-container", + ComponentUnion: dw.ComponentUnion{ + Container: &dw.ContainerComponent{ + Container: dw.Container{ + Image: "test-image:latest", + }, + }, + }, + }, + }, + }, + }, + }, + }, + Config: &v1alpha1.OperatorConfiguration{ + Workspace: &v1alpha1.WorkspaceConfig{ + PersistUserHome: &v1alpha1.PersistentHomeConfig{ + Enabled: pointer.Bool(true), + DisableInitContainer: pointer.Bool(false), + }, + InitContainers: []corev1.Container{}, + }, + }, + }, + expectDefaultInitAdded: true, + }, + { + name: "Skips default init when DisableInitContainer is true even with custom init", + workspace: &common.DevWorkspaceWithConfig{ + DevWorkspace: &dw.DevWorkspace{ + Spec: dw.DevWorkspaceSpec{ + Template: dw.DevWorkspaceTemplateSpec{ + DevWorkspaceTemplateSpecContent: dw.DevWorkspaceTemplateSpecContent{ + Components: []dw.Component{ + { + Name: "test-container", + ComponentUnion: dw.ComponentUnion{ + Container: &dw.ContainerComponent{ + Container: dw.Container{ + Image: "test-image:latest", + }, + }, + }, + }, + }, + }, + }, + }, + }, + Config: &v1alpha1.OperatorConfiguration{ + Workspace: &v1alpha1.WorkspaceConfig{ + PersistUserHome: &v1alpha1.PersistentHomeConfig{ + Enabled: pointer.Bool(true), + DisableInitContainer: pointer.Bool(true), + }, + }, + }, + }, + expectDefaultInitAdded: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := AddPersistentHomeVolume(tt.workspace) + assert.NoError(t, err) + assert.NotNil(t, result) + + // Check if default init component was added + hasDefaultInit := false + for _, component := range result.Components { + if component.Name == constants.HomeInitComponentName { + hasDefaultInit = true + break + } + } + + if tt.expectDefaultInitAdded { + assert.True(t, hasDefaultInit, "Expected default init component to be added") + } else { + assert.False(t, hasDefaultInit, "Expected default init component NOT to be added") + } + + // Verify persistent-home volume is always added + hasPersistentHomeVolume := false + for _, component := range result.Components { + if component.Name == constants.HomeVolumeName { + hasPersistentHomeVolume = true + break + } + } + assert.True(t, hasPersistentHomeVolume, "persistent-home volume should always be added") + }) + } +} + +func TestInferWorkspaceImage(t *testing.T) { + tests := []struct { + name string + template *dw.DevWorkspaceTemplateSpec + expectedImage string + }{ + { + name: "Returns first non-imported container image", + template: &dw.DevWorkspaceTemplateSpec{ + DevWorkspaceTemplateSpecContent: dw.DevWorkspaceTemplateSpecContent{ + Components: []dw.Component{ + { + Name: "main-container", + ComponentUnion: dw.ComponentUnion{ + Container: &dw.ContainerComponent{ + Container: dw.Container{ + Image: "my-workspace:latest", + }, + }, + }, + }, + }, + }, + }, + expectedImage: "my-workspace:latest", + }, + { + name: "Skips imported containers and returns first non-imported", + template: &dw.DevWorkspaceTemplateSpec{ + DevWorkspaceTemplateSpecContent: dw.DevWorkspaceTemplateSpecContent{ + Components: []dw.Component{ + { + Name: "plugin-container", + Attributes: attributes.Attributes{}. + PutString(constants.PluginSourceAttribute, "plugin-registry"), + ComponentUnion: dw.ComponentUnion{ + Container: &dw.ContainerComponent{ + Container: dw.Container{ + Image: "plugin-image:latest", + }, + }, + }, + }, + { + Name: "main-container", + ComponentUnion: dw.ComponentUnion{ + Container: &dw.ContainerComponent{ + Container: dw.Container{ + Image: "my-workspace:latest", + }, + }, + }, + }, + }, + }, + }, + expectedImage: "my-workspace:latest", + }, + { + name: "Returns empty string when no suitable container found", + template: &dw.DevWorkspaceTemplateSpec{ + DevWorkspaceTemplateSpecContent: dw.DevWorkspaceTemplateSpecContent{ + Components: []dw.Component{ + { + Name: "volume", + ComponentUnion: dw.ComponentUnion{ + Volume: &dw.VolumeComponent{}, + }, + }, + }, + }, + }, + expectedImage: "", + }, + { + name: "Returns empty string when all containers are imported", + template: &dw.DevWorkspaceTemplateSpec{ + DevWorkspaceTemplateSpecContent: dw.DevWorkspaceTemplateSpecContent{ + Components: []dw.Component{ + { + Name: "plugin-container", + Attributes: attributes.Attributes{}. + PutString(constants.PluginSourceAttribute, "plugin-registry"), + ComponentUnion: dw.ComponentUnion{ + Container: &dw.ContainerComponent{ + Container: dw.Container{ + Image: "plugin-image:latest", + }, + }, + }, + }, + }, + }, + }, + expectedImage: "", + }, + { + name: "Treats parent-sourced containers as non-imported", + template: &dw.DevWorkspaceTemplateSpec{ + DevWorkspaceTemplateSpecContent: dw.DevWorkspaceTemplateSpecContent{ + Components: []dw.Component{ + { + Name: "parent-container", + Attributes: attributes.Attributes{}. + PutString(constants.PluginSourceAttribute, "parent"), + ComponentUnion: dw.ComponentUnion{ + Container: &dw.ContainerComponent{ + Container: dw.Container{ + Image: "parent-image:latest", + }, + }, + }, + }, + }, + }, + }, + expectedImage: "parent-image:latest", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := InferWorkspaceImage(tt.template) + assert.Equal(t, tt.expectedImage, result) + }) + } +} diff --git a/test/e2e/pkg/client/oc.go b/test/e2e/pkg/client/oc.go index 419d99283..0d9e482a4 100644 --- a/test/e2e/pkg/client/oc.go +++ b/test/e2e/pkg/client/oc.go @@ -67,3 +67,13 @@ func (w *K8sClient) GetLogsForContainer(podName string, namespace, containerName outBytes, err := cmd.CombinedOutput() return string(outBytes), err } + +func (w *K8sClient) OcDeleteWorkspace(name, namespace string) (commandResult string, err error) { + cmd := exec.Command("bash", "-c", fmt.Sprintf( + "KUBECONFIG=%s oc delete devworkspace %s -n %s --ignore-not-found=true", + w.kubeCfgFile, + name, + namespace)) + outBytes, err := cmd.CombinedOutput() + return string(outBytes), err +} diff --git a/test/e2e/pkg/tests/custom_init_container_tests.go b/test/e2e/pkg/tests/custom_init_container_tests.go new file mode 100644 index 000000000..43a127228 --- /dev/null +++ b/test/e2e/pkg/tests/custom_init_container_tests.go @@ -0,0 +1,156 @@ +// +// Copyright (c) 2019-2025 Red Hat, Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package tests + +import ( + "context" + "fmt" + "os/exec" + "path/filepath" + "runtime" + + dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" + "github.com/devfile/devworkspace-operator/test/e2e/pkg/config" + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// getProjectRoot returns the project root directory by navigating up from this file. +func getProjectRoot() string { + _, filename, _, _ := runtime.Caller(0) + return filepath.Join(filepath.Dir(filename), "..", "..", "..", "..") +} + +var _ = ginkgo.Describe("[Custom Init Container Tests]", func() { + defer ginkgo.GinkgoRecover() + + ginkgo.It("Wait DevWorkspace Webhook Server Pod", func() { + controllerLabel := "app.kubernetes.io/name=devworkspace-webhook-server" + + deploy, err := config.AdminK8sClient.WaitForPodRunningByLabel(config.OperatorNamespace, controllerLabel) + if err != nil { + ginkgo.Fail(fmt.Sprintf("cannot get the Pod status with label %s: %s", controllerLabel, err.Error())) + return + } + + if !deploy { + ginkgo.Fail("DevWorkspace webhook didn't start properly") + } + }) + + ginkgo.Context("Custom init-persistent-home container", func() { + const workspaceName = "custom-init-test" + + ginkgo.BeforeEach(func() { + dwocFile := filepath.Join(getProjectRoot(), "test", "resources", "dwoc-custom-init.yaml") + cmd := exec.Command("kubectl", "apply", "-f", dwocFile) + output, err := cmd.CombinedOutput() + if err != nil { + ginkgo.Fail(fmt.Sprintf("Failed to apply DWOC: %s. Output: %s", err, string(output))) + } + }) + + ginkgo.It("Create workspace and verify custom init-persistent-home executed", func() { + workspaceFile := filepath.Join(getProjectRoot(), "test", "resources", "custom-init-test-workspace.yaml") + commandResult, err := config.DevK8sClient.OcApplyWorkspace(config.DevWorkspaceNamespace, workspaceFile) + if err != nil { + ginkgo.Fail(fmt.Sprintf("Failed to create workspace with custom init: %s %s", err.Error(), commandResult)) + return + } + + // Wait for workspace to be running + deploy, err := config.DevK8sClient.WaitDevWsStatus(workspaceName, config.DevWorkspaceNamespace, dw.DevWorkspaceStatusRunning) + if !deploy { + ginkgo.Fail(fmt.Sprintf("Workspace didn't start properly. Error: %s", err)) + } + + // Wait for pod to be running (may take a while for large image pulls) + podSelector := fmt.Sprintf("controller.devfile.io/devworkspace_name=%s", workspaceName) + var podName string + gomega.Eventually(func() error { + podName, err = config.AdminK8sClient.GetPodNameBySelector(podSelector, config.DevWorkspaceNamespace) + return err + }, "10m", "5s").Should(gomega.Succeed()) + + // Check that the custom init script ran by verifying the marker file exists + resultOfExecCommand, err := config.DevK8sClient.ExecCommandInContainer(podName, config.DevWorkspaceNamespace, "tooling", "test -f /home/user/.custom_init_complete && echo 'SUCCESS' || echo 'FAILED'") + if err != nil { + ginkgo.Fail(fmt.Sprintf("Cannot execute command in container. Error: `%s`. Exec output: `%s`", err, resultOfExecCommand)) + } + gomega.Expect(resultOfExecCommand).To(gomega.ContainSubstring("SUCCESS")) + }) + + ginkgo.AfterEach(func() { + // Cleanup workspace + _, _ = config.DevK8sClient.OcDeleteWorkspace(workspaceName, config.DevWorkspaceNamespace) + }) + }) + + ginkgo.Context("DisableInitContainer flag behavior", func() { + const workspaceName = "disabled-init-test" + + ginkgo.BeforeEach(func() { + dwocFile := filepath.Join(getProjectRoot(), "test", "resources", "dwoc-disabled-init.yaml") + cmd := exec.Command("kubectl", "apply", "-f", dwocFile) + output, err := cmd.CombinedOutput() + if err != nil { + ginkgo.Fail(fmt.Sprintf("Failed to apply DWOC: %s. Output: %s", err, string(output))) + } + }) + + ginkgo.It("Create workspace and verify no init-persistent-home container present", func() { + workspaceFile := filepath.Join(getProjectRoot(), "test", "resources", "disabled-init-test-workspace.yaml") + commandResult, err := config.DevK8sClient.OcApplyWorkspace(config.DevWorkspaceNamespace, workspaceFile) + if err != nil { + ginkgo.Fail(fmt.Sprintf("Failed to create workspace: %s %s", err.Error(), commandResult)) + return + } + + // Wait for workspace to be running + deploy, err := config.DevK8sClient.WaitDevWsStatus(workspaceName, config.DevWorkspaceNamespace, dw.DevWorkspaceStatusRunning) + if !deploy { + ginkgo.Fail(fmt.Sprintf("Workspace didn't start properly. Error: %s", err)) + } + + // Wait for pod to be running (may take a while for large image pulls) + podSelector := fmt.Sprintf("controller.devfile.io/devworkspace_name=%s", workspaceName) + var podName string + gomega.Eventually(func() error { + podName, err = config.AdminK8sClient.GetPodNameBySelector(podSelector, config.DevWorkspaceNamespace) + return err + }, "10m", "5s").Should(gomega.Succeed()) + + // Get pod spec and check init containers + pod, err := config.AdminK8sClient.Kube().CoreV1().Pods(config.DevWorkspaceNamespace).Get(context.TODO(), podName, metav1.GetOptions{}) + if err != nil { + ginkgo.Fail(fmt.Sprintf("Cannot get pod. Error: %s", err)) + } + + // Verify no init container named init-persistent-home + for _, initContainer := range pod.Spec.InitContainers { + if initContainer.Name == "init-persistent-home" { + ginkgo.Fail("init-persistent-home container should not be present when disableInitContainer is true") + } + } + }) + + ginkgo.AfterEach(func() { + // Cleanup workspace + _, _ = config.DevK8sClient.OcDeleteWorkspace(workspaceName, config.DevWorkspaceNamespace) + }) + }) +}) diff --git a/test/resources/custom-init-test-workspace.yaml b/test/resources/custom-init-test-workspace.yaml new file mode 100644 index 000000000..948f684d9 --- /dev/null +++ b/test/resources/custom-init-test-workspace.yaml @@ -0,0 +1,16 @@ +kind: DevWorkspace +apiVersion: workspace.devfile.io/v1alpha2 +metadata: + name: custom-init-test +spec: + started: true + routingClass: 'basic' + template: + attributes: + controller.devfile.io/storage-type: per-user + components: + - name: tooling + container: + image: quay.io/devfile/universal-developer-image:latest + memoryLimit: 2Gi + mountSources: true diff --git a/test/resources/disabled-init-test-workspace.yaml b/test/resources/disabled-init-test-workspace.yaml new file mode 100644 index 000000000..af3a26e46 --- /dev/null +++ b/test/resources/disabled-init-test-workspace.yaml @@ -0,0 +1,16 @@ +kind: DevWorkspace +apiVersion: workspace.devfile.io/v1alpha2 +metadata: + name: disabled-init-test +spec: + started: true + routingClass: 'basic' + template: + attributes: + controller.devfile.io/storage-type: per-user + components: + - name: tooling + container: + image: quay.io/devfile/universal-developer-image:latest + memoryLimit: 2Gi + mountSources: true diff --git a/test/resources/dwoc-custom-init.yaml b/test/resources/dwoc-custom-init.yaml new file mode 100644 index 000000000..f8e716e55 --- /dev/null +++ b/test/resources/dwoc-custom-init.yaml @@ -0,0 +1,15 @@ +apiVersion: controller.devfile.io/v1alpha1 +kind: DevWorkspaceOperatorConfig +metadata: + name: devworkspace-operator-config + namespace: openshift-operators +config: + workspace: + initContainers: + - name: init-persistent-home + args: + - | + echo "Custom init running" + touch /home/user/.custom_init_complete + persistUserHome: + enabled: true diff --git a/test/resources/dwoc-disabled-init.yaml b/test/resources/dwoc-disabled-init.yaml new file mode 100644 index 000000000..b8bebd6e3 --- /dev/null +++ b/test/resources/dwoc-disabled-init.yaml @@ -0,0 +1,10 @@ +apiVersion: controller.devfile.io/v1alpha1 +kind: DevWorkspaceOperatorConfig +metadata: + name: devworkspace-operator-config + namespace: openshift-operators +config: + workspace: + persistUserHome: + enabled: true + disableInitContainer: true From ef4b2ea454c0cb7776f71ff8919c122533539aeb Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Fri, 31 Oct 2025 12:36:42 +0200 Subject: [PATCH 04/30] Update apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go Co-authored-by: Rohan Kumar Signed-off-by: Oleksii Kurinnyi --- apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go b/apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go index a1dd397fe..e5076b803 100644 --- a/apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go +++ b/apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go @@ -203,7 +203,7 @@ type WorkspaceConfig struct { // If the feature is disabled, setting this field may cause an endless workspace start loop. // +kubebuilder:validation:Optional HostUsers *bool `json:"hostUsers,omitempty"` - // InitContainers are injected into all workspace pods as Kubernetes init containers. + // InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. // Typical uses: injecting organization tools/configs, initializing persistent home, etc. // Note: Only trusted administrators should be allowed to edit the DevWorkspaceOperatorConfig. InitContainers []corev1.Container `json:"initContainers,omitempty"` From 105287adf09e9f9ef8ba031231b97e513b11dcf7 Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Fri, 31 Oct 2025 12:51:58 +0200 Subject: [PATCH 05/30] Apply suggestion from @rohanKanojia Co-authored-by: Rohan Kumar Signed-off-by: Oleksii Kurinnyi --- pkg/library/home/util.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/library/home/util.go b/pkg/library/home/util.go index 521624cb2..86378d0ae 100644 --- a/pkg/library/home/util.go +++ b/pkg/library/home/util.go @@ -22,7 +22,8 @@ import ( // InferWorkspaceImage finds the first non-imported container component image in the // flattened devfile template. This mirrors the selection rule used by the built-in -// persistent-home initializer to pick a "primary" workspace image. +// persistent-home initializer to pick a "primary" workspace image. +// If no such component exists, it returns an empty string. func InferWorkspaceImage(dwTemplate *v1alpha2.DevWorkspaceTemplateSpec) string { for _, component := range dwTemplate.Components { if component.Container == nil { From 939a9dd51b7c826ac5d72a20283cc6e12af113a4 Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Fri, 31 Oct 2025 14:39:36 +0200 Subject: [PATCH 06/30] fixup! feat: configure init containers Signed-off-by: Oleksii Kurinnyi --- .../workspace/devworkspace_controller.go | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/controllers/workspace/devworkspace_controller.go b/controllers/workspace/devworkspace_controller.go index b645e0284..46a65d6e5 100644 --- a/controllers/workspace/devworkspace_controller.go +++ b/controllers/workspace/devworkspace_controller.go @@ -73,7 +73,7 @@ func applyHomeInitDefaults(c corev1.Container, workspace *common.DevWorkspaceWit if c.Image == "" { inferred := home.InferWorkspaceImage(&workspace.Spec.Template) if inferred == "" { - return c, fmt.Errorf("unable to infer workspace image for init-persistent-home; specify image explicitly") + return c, fmt.Errorf("unable to infer workspace image for %s; specify image explicitly", constants.HomeInitComponentName) } c.Image = inferred } @@ -87,34 +87,38 @@ func applyHomeInitDefaults(c corev1.Container, workspace *common.DevWorkspaceWit // does not use advanced Kubernetes container fields that could make behavior unpredictable. func validateNoAdvancedFields(c corev1.Container) error { if len(c.Ports) > 0 { - return fmt.Errorf("ports are not allowed for init-persistent-home") + return fmt.Errorf("ports are not allowed for %s", constants.HomeInitComponentName) } if c.LivenessProbe != nil || c.ReadinessProbe != nil || c.StartupProbe != nil { - return fmt.Errorf("probes are not allowed for init-persistent-home") + return fmt.Errorf("probes are not allowed for %s", constants.HomeInitComponentName) } if c.Lifecycle != nil { - return fmt.Errorf("lifecycle hooks are not allowed for init-persistent-home") + return fmt.Errorf("lifecycle hooks are not allowed for %s", constants.HomeInitComponentName) } if c.Stdin || c.StdinOnce || c.TTY { - return fmt.Errorf("stdin/tty fields are not allowed for init-persistent-home") + return fmt.Errorf("stdin/tty fields are not allowed for %s", constants.HomeInitComponentName) } - if len(c.VolumeDevices) > 0 || c.WorkingDir != "" { - return fmt.Errorf("volumeDevices and workingDir are not allowed for init-persistent-home") + if len(c.VolumeDevices) > 0 { + return fmt.Errorf("volumeDevices are not allowed for %s", constants.HomeInitComponentName) + } + + if c.WorkingDir != "" { + return fmt.Errorf("workingDir is not allowed for %s", constants.HomeInitComponentName) } if c.TerminationMessagePath != "" || c.TerminationMessagePolicy != "" { - return fmt.Errorf("termination message fields are not allowed for init-persistent-home") + return fmt.Errorf("termination message fields are not allowed for %s", constants.HomeInitComponentName) } if c.SecurityContext != nil { - return fmt.Errorf("securityContext is not allowed for init-persistent-home") + return fmt.Errorf("securityContext is not allowed for %s", constants.HomeInitComponentName) } if c.Resources.Limits != nil || c.Resources.Requests != nil { - return fmt.Errorf("resource limits/requests are not allowed for init-persistent-home") + return fmt.Errorf("resource limits/requests are not allowed for %s", constants.HomeInitComponentName) } return nil @@ -123,19 +127,19 @@ func validateNoAdvancedFields(c corev1.Container) error { // validateHomeInitContainer validates all aspects of the init-persistent-home container. func validateHomeInitContainer(c corev1.Container) error { if strings.ContainsAny(c.Image, "\n\r\t ") { - return fmt.Errorf("invalid image reference for init-persistent-home: image reference contains invalid whitespace characters") + return fmt.Errorf("invalid image reference for %s: image reference contains invalid whitespace characters", constants.HomeInitComponentName) } if len(c.Command) != 2 || c.Command[0] != "/bin/sh" || c.Command[1] != "-c" { - return fmt.Errorf("command must be exactly [/bin/sh, -c]") + return fmt.Errorf("command must be exactly [/bin/sh, -c] for %s", constants.HomeInitComponentName) } if len(c.Args) != 1 { - return fmt.Errorf("args must contain exactly one script string") + return fmt.Errorf("args must contain exactly one script string for %s", constants.HomeInitComponentName) } if len(c.VolumeMounts) > 0 { - return fmt.Errorf("volumeMounts are not allowed for init-persistent-home; persistent-home is auto-mounted at /home/user/") + return fmt.Errorf("volumeMounts are not allowed for %s; persistent-home is auto-mounted at /home/user/", constants.HomeInitComponentName) } if err := validateNoAdvancedFields(c); err != nil { @@ -489,7 +493,7 @@ func (r *DevWorkspaceReconciler) Reconcile(ctx context.Context, req ctrl.Request // Apply defaults and validation for init-persistent-home validated, err := defaultAndValidateHomeInitContainer(c, workspace) if err != nil { - return r.failWorkspace(workspace, fmt.Sprintf("Invalid init-persistent-home container: %s", err), metrics.ReasonBadRequest, reqLogger, &reconcileStatus), nil + return r.failWorkspace(workspace, fmt.Sprintf("Invalid %s container: %s", constants.HomeInitComponentName, err), metrics.ReasonBadRequest, reqLogger, &reconcileStatus), nil } c = validated } From 9243abc1b39e3be3a66e6c390f7bb5ba515943f4 Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Fri, 31 Oct 2025 15:00:28 +0200 Subject: [PATCH 07/30] fixup! fixup! feat: configure init containers Signed-off-by: Oleksii Kurinnyi --- pkg/library/home/util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/library/home/util.go b/pkg/library/home/util.go index 86378d0ae..ec9868dd0 100644 --- a/pkg/library/home/util.go +++ b/pkg/library/home/util.go @@ -22,7 +22,7 @@ import ( // InferWorkspaceImage finds the first non-imported container component image in the // flattened devfile template. This mirrors the selection rule used by the built-in -// persistent-home initializer to pick a "primary" workspace image. +// persistent-home initializer to pick a "primary" workspace image. // If no such component exists, it returns an empty string. func InferWorkspaceImage(dwTemplate *v1alpha2.DevWorkspaceTemplateSpec) string { for _, component := range dwTemplate.Components { From 366b021584f47061c8f8c7fff07bb8fbf5a1f79b Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Fri, 31 Oct 2025 15:56:12 +0200 Subject: [PATCH 08/30] fixup! fixup! fixup! feat: configure init containers Signed-off-by: Oleksii Kurinnyi --- .../controller.devfile.io_devworkspaceoperatorconfigs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml b/deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml index 7fe958169..7e275cbc0 100644 --- a/deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml +++ b/deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml @@ -2555,7 +2555,7 @@ spec: type: string initContainers: description: |- - InitContainers are injected into all workspace pods as Kubernetes init containers. + InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. Typical uses: injecting organization tools/configs, initializing persistent home, etc. Note: Only trusted administrators should be allowed to edit the DevWorkspaceOperatorConfig. items: From e35135468d6ff1050a8d098a7d4232201329891d Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Fri, 31 Oct 2025 16:00:23 +0200 Subject: [PATCH 09/30] fixup! fixup! fixup! fixup! feat: configure init containers Signed-off-by: Oleksii Kurinnyi --- .../controller.devfile.io_devworkspaceoperatorconfigs.yaml | 2 +- deploy/deployment/kubernetes/combined.yaml | 2 +- ...rconfigs.controller.devfile.io.CustomResourceDefinition.yaml | 2 +- deploy/deployment/openshift/combined.yaml | 2 +- ...rconfigs.controller.devfile.io.CustomResourceDefinition.yaml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml b/deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml index 3577551b5..7a4c082b0 100644 --- a/deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml +++ b/deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml @@ -2427,7 +2427,7 @@ spec: type: string initContainers: description: |- - InitContainers are injected into all workspace pods as Kubernetes init containers. + InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. Typical uses: injecting organization tools/configs, initializing persistent home, etc. Note: Only trusted administrators should be allowed to edit the DevWorkspaceOperatorConfig. items: diff --git a/deploy/deployment/kubernetes/combined.yaml b/deploy/deployment/kubernetes/combined.yaml index 69ccbf682..6bfc9a5c2 100644 --- a/deploy/deployment/kubernetes/combined.yaml +++ b/deploy/deployment/kubernetes/combined.yaml @@ -2557,7 +2557,7 @@ spec: type: string initContainers: description: |- - InitContainers are injected into all workspace pods as Kubernetes init containers. + InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. Typical uses: injecting organization tools/configs, initializing persistent home, etc. Note: Only trusted administrators should be allowed to edit the DevWorkspaceOperatorConfig. items: diff --git a/deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml b/deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml index 34b9e2e78..650760b18 100644 --- a/deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml +++ b/deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml @@ -2557,7 +2557,7 @@ spec: type: string initContainers: description: |- - InitContainers are injected into all workspace pods as Kubernetes init containers. + InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. Typical uses: injecting organization tools/configs, initializing persistent home, etc. Note: Only trusted administrators should be allowed to edit the DevWorkspaceOperatorConfig. items: diff --git a/deploy/deployment/openshift/combined.yaml b/deploy/deployment/openshift/combined.yaml index 60acc6c28..daefe944d 100644 --- a/deploy/deployment/openshift/combined.yaml +++ b/deploy/deployment/openshift/combined.yaml @@ -2557,7 +2557,7 @@ spec: type: string initContainers: description: |- - InitContainers are injected into all workspace pods as Kubernetes init containers. + InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. Typical uses: injecting organization tools/configs, initializing persistent home, etc. Note: Only trusted administrators should be allowed to edit the DevWorkspaceOperatorConfig. items: diff --git a/deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml b/deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml index 34b9e2e78..650760b18 100644 --- a/deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml +++ b/deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml @@ -2557,7 +2557,7 @@ spec: type: string initContainers: description: |- - InitContainers are injected into all workspace pods as Kubernetes init containers. + InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. Typical uses: injecting organization tools/configs, initializing persistent home, etc. Note: Only trusted administrators should be allowed to edit the DevWorkspaceOperatorConfig. items: From 855e02d063b2565544aec7fbd983cf0f4e2976ad Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Fri, 31 Oct 2025 16:32:30 +0200 Subject: [PATCH 10/30] fixup! fixup! fixup! fixup! fixup! feat: configure init containers Signed-off-by: Oleksii Kurinnyi --- controllers/workspace/init_container_validation_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/workspace/init_container_validation_test.go b/controllers/workspace/init_container_validation_test.go index dbfd290cd..8610e6ea6 100644 --- a/controllers/workspace/init_container_validation_test.go +++ b/controllers/workspace/init_container_validation_test.go @@ -236,7 +236,7 @@ func TestDefaultAndValidateHomeInitContainer(t *testing.T) { WorkingDir: "/tmp", }, expectError: true, - errorMsg: "volumeDevices and workingDir are not allowed", + errorMsg: "workingDir is not allowed", }, { name: "Rejects image with whitespace", From 7b1a43737ba5c713cb61963ee140a525b5f63701 Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Wed, 19 Nov 2025 13:50:18 +0200 Subject: [PATCH 11/30] feat: add strategic merge for init containers Signed-off-by: Oleksii Kurinnyi --- .../workspace/devworkspace_controller.go | 32 ++++- pkg/library/initcontainers/merge.go | 105 ++++++++++++++++ pkg/library/initcontainers/merge_test.go | 115 ++++++++++++++++++ 3 files changed, 250 insertions(+), 2 deletions(-) create mode 100644 pkg/library/initcontainers/merge.go create mode 100644 pkg/library/initcontainers/merge_test.go diff --git a/controllers/workspace/devworkspace_controller.go b/controllers/workspace/devworkspace_controller.go index 46a65d6e5..e1b3ec0ea 100644 --- a/controllers/workspace/devworkspace_controller.go +++ b/controllers/workspace/devworkspace_controller.go @@ -22,6 +22,7 @@ import ( "strings" "time" + "github.com/devfile/devworkspace-operator/pkg/library/initcontainers" "github.com/devfile/devworkspace-operator/pkg/library/ssh" dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" @@ -474,11 +475,13 @@ func (r *DevWorkspaceReconciler) Reconcile(ctx context.Context, req ctrl.Request } // Inject operator-configured init containers - if workspace.Config != nil && workspace.Config.Workspace != nil { + if workspace.Config != nil && workspace.Config.Workspace != nil && len(workspace.Config.Workspace.InitContainers) > 0 { // Check if init-persistent-home should be disabled disableHomeInit := workspace.Config.Workspace.PersistUserHome.DisableInitContainer != nil && *workspace.Config.Workspace.PersistUserHome.DisableInitContainer + // Prepare patches: filter and preprocess init containers from config + patches := []corev1.Container{} for _, c := range workspace.Config.Workspace.InitContainers { // Special handling for init-persistent-home if c.Name == constants.HomeInitComponentName { @@ -497,8 +500,33 @@ func (r *DevWorkspaceReconciler) Reconcile(ctx context.Context, req ctrl.Request } c = validated } - devfilePodAdditions.InitContainers = append(devfilePodAdditions.InitContainers, c) + patches = append(patches, c) } + + // Perform strategic merge + merged, err := initcontainers.MergeInitContainers(devfilePodAdditions.InitContainers, patches) + if err != nil { + return r.failWorkspace(workspace, fmt.Sprintf("Failed to merge init containers: %s", err), metrics.ReasonBadRequest, reqLogger, &reconcileStatus), nil + } + + // Ensure init-persistent-home container have correct fields after merge + for i := range merged { + if merged[i].Name == constants.HomeInitComponentName { + // Ensure Command is correct (should be set by defaultAndValidateHomeInitContainer, but enforce after merge) + merged[i].Command = []string{"/bin/sh", "-c"} + // Args should be set by patch validation, but ensure it has exactly one element + if len(merged[i].Args) != 1 { + return r.failWorkspace(workspace, fmt.Sprintf("Invalid %s container: args must contain exactly one script string", constants.HomeInitComponentName), metrics.ReasonBadRequest, reqLogger, &reconcileStatus), nil + } + // Ensure VolumeMounts are correct + merged[i].VolumeMounts = []corev1.VolumeMount{{ + Name: constants.HomeVolumeName, + MountPath: constants.HomeUserDirectory, + }} + } + } + + devfilePodAdditions.InitContainers = merged } // Add ServiceAccount tokens into devfile containers diff --git a/pkg/library/initcontainers/merge.go b/pkg/library/initcontainers/merge.go new file mode 100644 index 000000000..367ca4904 --- /dev/null +++ b/pkg/library/initcontainers/merge.go @@ -0,0 +1,105 @@ +// +// Copyright (c) 2019-2025 Red Hat, Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package initcontainers + +import ( + "fmt" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/json" + "k8s.io/apimachinery/pkg/util/strategicpatch" +) + +// MergeInitContainers performs a strategic merge of init containers. +// Containers with the same name in the patch will be merged into the base, +// and new containers will be appended. The merge uses Kubernetes' strategic +// merge patch semantics with name as the merge key. +func MergeInitContainers(base []corev1.Container, patches []corev1.Container) ([]corev1.Container, error) { + if len(patches) == 0 { + return base, nil + } + + // create PodSpec structure with base init containers + basePodSpec := corev1.PodSpec{ + InitContainers: base, + } + + // create PodSpec structure with patch init containers + patchPodSpec := corev1.PodSpec{ + InitContainers: patches, + } + + // marshal both structures to JSON + baseBytes, err := json.Marshal(basePodSpec) + if err != nil { + return nil, fmt.Errorf("failed to marshal base init containers: %w", err) + } + patchBytes, err := json.Marshal(patchPodSpec) + if err != nil { + return nil, fmt.Errorf("failed to marshal patch init containers: %w", err) + } + + // perform strategic merge patch + mergedBytes, err := strategicpatch.StrategicMergePatch( + baseBytes, + patchBytes, + &corev1.PodSpec{}, + ) + if err != nil { + return nil, fmt.Errorf("failed to apply strategic merge patch: %w", err) + } + + // unmarshal the merged result + var mergedPodSpec corev1.PodSpec + if err := json.Unmarshal(mergedBytes, &mergedPodSpec); err != nil { + return nil, fmt.Errorf("failed to unmarshal merged init containers: %w", err) + } + + /* restore the original containers order */ + + // build map for quick lookup + mergedMap := make(map[string]corev1.Container) + for _, container := range mergedPodSpec.InitContainers { + mergedMap[container.Name] = container + } + + result := make([]corev1.Container, 0, len(mergedPodSpec.InitContainers)) + baseNames := make(map[string]bool) + + // add base containers in order, merged one if patched + for _, baseContainer := range base { + baseNames[baseContainer.Name] = true + if merged, exists := mergedMap[baseContainer.Name]; exists { + result = append(result, merged) + delete(mergedMap, baseContainer.Name) + } else { + result = append(result, baseContainer) + } + } + + // append new containers from patches + for _, patchContainer := range patches { + if !baseNames[patchContainer.Name] { + if merged, exists := mergedMap[patchContainer.Name]; exists { + result = append(result, merged) + } else { + result = append(result, patchContainer) + } + } + } + + return result, nil +} diff --git a/pkg/library/initcontainers/merge_test.go b/pkg/library/initcontainers/merge_test.go new file mode 100644 index 000000000..825e8dc85 --- /dev/null +++ b/pkg/library/initcontainers/merge_test.go @@ -0,0 +1,115 @@ +// +// Copyright (c) 2019-2025 Red Hat, Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package initcontainers + +import ( + "testing" + + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" +) + +func TestMergeInitContainers(t *testing.T) { + tests := []struct { + name string + base []corev1.Container + patches []corev1.Container + want []corev1.Container + wantErr bool + }{ + { + name: "empty base", + base: []corev1.Container{}, + patches: []corev1.Container{ + {Name: "new-container", Image: "new-image"}, + }, + want: []corev1.Container{ + {Name: "new-container", Image: "new-image"}, + }, + wantErr: false, + }, + { + name: "empty patches", + base: []corev1.Container{ + {Name: "base-container", Image: "base-image"}, + }, + patches: []corev1.Container{}, + want: []corev1.Container{ + {Name: "base-container", Image: "base-image"}, + }, + wantErr: false, + }, + { + name: "multiple containers", + base: []corev1.Container{ + {Name: "first", Image: "first-image"}, + {Name: "second", Image: "second-image"}, + {Name: "third", Image: "third-image"}, + }, + patches: []corev1.Container{ + {Name: "new-container", Image: "new-image"}, + {Name: "second", Image: "updated-second-image"}, + }, + want: []corev1.Container{ + {Name: "first", Image: "first-image"}, + {Name: "second", Image: "updated-second-image"}, + {Name: "third", Image: "third-image"}, + {Name: "new-container", Image: "new-image"}, + }, + wantErr: false, + }, + { + name: "partial field merge", + base: []corev1.Container{ + { + Name: "base-container", + Image: "base-image", + Command: []string{"/bin/sh", "-c"}, + Args: []string{"echo 'base'"}, + Env: []corev1.EnvVar{{Name: "BASE_VAR", Value: "base-value"}}, + }, + }, + patches: []corev1.Container{ + { + Name: "base-container", + Args: []string{"echo 'patched'"}, // only this field changed + }, + }, + want: []corev1.Container{ + { + Name: "base-container", + Image: "base-image", + Command: []string{"/bin/sh", "-c"}, + Args: []string{"echo 'patched'"}, + Env: []corev1.EnvVar{{Name: "BASE_VAR", Value: "base-value"}}, + }, + }, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := MergeInitContainers(tt.base, tt.patches) + if tt.wantErr { + assert.Error(t, err, "should return error") + } else { + assert.NoError(t, err, "should not return error") + assert.Equal(t, tt.want, got, "should return merged containers") + } + }) + } +} From 30777bcdfaf85455ac90c730974161bfe1298aee Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Wed, 19 Nov 2025 13:58:51 +0200 Subject: [PATCH 12/30] Apply suggestion from @tolusha Co-authored-by: Anatolii Bazko Signed-off-by: Oleksii Kurinnyi --- apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go b/apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go index e5076b803..829b888aa 100644 --- a/apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go +++ b/apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go @@ -205,7 +205,7 @@ type WorkspaceConfig struct { HostUsers *bool `json:"hostUsers,omitempty"` // InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. // Typical uses: injecting organization tools/configs, initializing persistent home, etc. - // Note: Only trusted administrators should be allowed to edit the DevWorkspaceOperatorConfig. + // Note: Only administrators should be allowed to edit the DevWorkspaceOperatorConfig. InitContainers []corev1.Container `json:"initContainers,omitempty"` } From b0aa34da70807e8d10f5de9fd5de4bccc169f768 Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Wed, 19 Nov 2025 14:21:41 +0200 Subject: [PATCH 13/30] fix: extract ensureHomeInitContainerFields to remove duplication Signed-off-by: Oleksii Kurinnyi --- .../workspace/devworkspace_controller.go | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/controllers/workspace/devworkspace_controller.go b/controllers/workspace/devworkspace_controller.go index e1b3ec0ea..5be61d0fc 100644 --- a/controllers/workspace/devworkspace_controller.go +++ b/controllers/workspace/devworkspace_controller.go @@ -150,6 +150,20 @@ func validateHomeInitContainer(c corev1.Container) error { return nil } +// ensureHomeInitContainerFields ensures that an init-persistent-home container has +// the correct Command, Args, and VolumeMounts. +func ensureHomeInitContainerFields(c *corev1.Container) error { + c.Command = []string{"/bin/sh", "-c"} + if len(c.Args) != 1 { + return fmt.Errorf("args must contain exactly one script string for %s", constants.HomeInitComponentName) + } + c.VolumeMounts = []corev1.VolumeMount{{ + Name: constants.HomeVolumeName, + MountPath: constants.HomeUserDirectory, + }} + return nil +} + // defaultAndValidateHomeInitContainer applies defaults and validation for a custom // DWOC-provided init container named init-persistent-home. It ensures a shell execution // model, a single script arg, injects the persistent-home mount at /home/user/, and @@ -165,10 +179,9 @@ func defaultAndValidateHomeInitContainer(c corev1.Container, workspace *common.D return c, err } - c.VolumeMounts = []corev1.VolumeMount{{ - Name: constants.HomeVolumeName, - MountPath: constants.HomeUserDirectory, - }} + if err = ensureHomeInitContainerFields(&c); err != nil { + return c, err + } return c, nil } @@ -512,17 +525,9 @@ func (r *DevWorkspaceReconciler) Reconcile(ctx context.Context, req ctrl.Request // Ensure init-persistent-home container have correct fields after merge for i := range merged { if merged[i].Name == constants.HomeInitComponentName { - // Ensure Command is correct (should be set by defaultAndValidateHomeInitContainer, but enforce after merge) - merged[i].Command = []string{"/bin/sh", "-c"} - // Args should be set by patch validation, but ensure it has exactly one element - if len(merged[i].Args) != 1 { - return r.failWorkspace(workspace, fmt.Sprintf("Invalid %s container: args must contain exactly one script string", constants.HomeInitComponentName), metrics.ReasonBadRequest, reqLogger, &reconcileStatus), nil + if err := ensureHomeInitContainerFields(&merged[i]); err != nil { + return r.failWorkspace(workspace, fmt.Sprintf("Invalid %s container: %s", constants.HomeInitComponentName, err), metrics.ReasonBadRequest, reqLogger, &reconcileStatus), nil } - // Ensure VolumeMounts are correct - merged[i].VolumeMounts = []corev1.VolumeMount{{ - Name: constants.HomeVolumeName, - MountPath: constants.HomeUserDirectory, - }} } } From bd287163f046deaa34601b432aee26870068db81 Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Wed, 19 Nov 2025 15:38:47 +0200 Subject: [PATCH 14/30] fixes Signed-off-by: Oleksii Kurinnyi --- controllers/workspace/devworkspace_controller.go | 14 +++++++------- ...ler.devfile.io_devworkspaceoperatorconfigs.yaml | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/controllers/workspace/devworkspace_controller.go b/controllers/workspace/devworkspace_controller.go index 5be61d0fc..d123ccd6a 100644 --- a/controllers/workspace/devworkspace_controller.go +++ b/controllers/workspace/devworkspace_controller.go @@ -490,30 +490,30 @@ func (r *DevWorkspaceReconciler) Reconcile(ctx context.Context, req ctrl.Request // Inject operator-configured init containers if workspace.Config != nil && workspace.Config.Workspace != nil && len(workspace.Config.Workspace.InitContainers) > 0 { // Check if init-persistent-home should be disabled - disableHomeInit := workspace.Config.Workspace.PersistUserHome.DisableInitContainer != nil && - *workspace.Config.Workspace.PersistUserHome.DisableInitContainer + disableHomeInit := pointer.BoolDeref(workspace.Config.Workspace.PersistUserHome.DisableInitContainer, false) // Prepare patches: filter and preprocess init containers from config patches := []corev1.Container{} - for _, c := range workspace.Config.Workspace.InitContainers { + for _, container := range workspace.Config.Workspace.InitContainers { // Special handling for init-persistent-home - if c.Name == constants.HomeInitComponentName { + if container.Name == constants.HomeInitComponentName { // Skip if persistent home is disabled if !home.PersistUserHomeEnabled(workspace) { + reqLogger.Info("Skipping init-persistent-home container: persistent home is disabled") continue } // Skip if init container is explicitly disabled if disableHomeInit { + reqLogger.Info("Skipping init-persistent-home container: DisableInitContainer is true") continue } // Apply defaults and validation for init-persistent-home - validated, err := defaultAndValidateHomeInitContainer(c, workspace) + container, err = defaultAndValidateHomeInitContainer(container, workspace) if err != nil { return r.failWorkspace(workspace, fmt.Sprintf("Invalid %s container: %s", constants.HomeInitComponentName, err), metrics.ReasonBadRequest, reqLogger, &reconcileStatus), nil } - c = validated } - patches = append(patches, c) + patches = append(patches, container) } // Perform strategic merge diff --git a/deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml b/deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml index 7e275cbc0..f42c03b3e 100644 --- a/deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml +++ b/deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml @@ -2557,7 +2557,7 @@ spec: description: |- InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. Typical uses: injecting organization tools/configs, initializing persistent home, etc. - Note: Only trusted administrators should be allowed to edit the DevWorkspaceOperatorConfig. + Note: Only administrators should be allowed to edit the DevWorkspaceOperatorConfig. items: description: A single application container that you want to run within a pod. From 8f042993f69577545a4e88c4c904803b71f3648e Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Wed, 19 Nov 2025 15:47:39 +0200 Subject: [PATCH 15/30] fixup! fixes Signed-off-by: Oleksii Kurinnyi --- .../controller.devfile.io_devworkspaceoperatorconfigs.yaml | 2 +- deploy/deployment/kubernetes/combined.yaml | 2 +- ...rconfigs.controller.devfile.io.CustomResourceDefinition.yaml | 2 +- deploy/deployment/openshift/combined.yaml | 2 +- ...rconfigs.controller.devfile.io.CustomResourceDefinition.yaml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml b/deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml index 7a4c082b0..c4f476950 100644 --- a/deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml +++ b/deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml @@ -2429,7 +2429,7 @@ spec: description: |- InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. Typical uses: injecting organization tools/configs, initializing persistent home, etc. - Note: Only trusted administrators should be allowed to edit the DevWorkspaceOperatorConfig. + Note: Only administrators should be allowed to edit the DevWorkspaceOperatorConfig. items: description: A single application container that you want to run within a pod. properties: diff --git a/deploy/deployment/kubernetes/combined.yaml b/deploy/deployment/kubernetes/combined.yaml index 6bfc9a5c2..d2053523e 100644 --- a/deploy/deployment/kubernetes/combined.yaml +++ b/deploy/deployment/kubernetes/combined.yaml @@ -2559,7 +2559,7 @@ spec: description: |- InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. Typical uses: injecting organization tools/configs, initializing persistent home, etc. - Note: Only trusted administrators should be allowed to edit the DevWorkspaceOperatorConfig. + Note: Only administrators should be allowed to edit the DevWorkspaceOperatorConfig. items: description: A single application container that you want to run within a pod. diff --git a/deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml b/deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml index 650760b18..bca0eae00 100644 --- a/deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml +++ b/deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml @@ -2559,7 +2559,7 @@ spec: description: |- InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. Typical uses: injecting organization tools/configs, initializing persistent home, etc. - Note: Only trusted administrators should be allowed to edit the DevWorkspaceOperatorConfig. + Note: Only administrators should be allowed to edit the DevWorkspaceOperatorConfig. items: description: A single application container that you want to run within a pod. diff --git a/deploy/deployment/openshift/combined.yaml b/deploy/deployment/openshift/combined.yaml index daefe944d..e560caa99 100644 --- a/deploy/deployment/openshift/combined.yaml +++ b/deploy/deployment/openshift/combined.yaml @@ -2559,7 +2559,7 @@ spec: description: |- InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. Typical uses: injecting organization tools/configs, initializing persistent home, etc. - Note: Only trusted administrators should be allowed to edit the DevWorkspaceOperatorConfig. + Note: Only administrators should be allowed to edit the DevWorkspaceOperatorConfig. items: description: A single application container that you want to run within a pod. diff --git a/deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml b/deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml index 650760b18..bca0eae00 100644 --- a/deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml +++ b/deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml @@ -2559,7 +2559,7 @@ spec: description: |- InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. Typical uses: injecting organization tools/configs, initializing persistent home, etc. - Note: Only trusted administrators should be allowed to edit the DevWorkspaceOperatorConfig. + Note: Only administrators should be allowed to edit the DevWorkspaceOperatorConfig. items: description: A single application container that you want to run within a pod. From dfc4a73e4320413987d992dc0c43600fa1e498f8 Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Tue, 25 Nov 2025 13:57:28 +0200 Subject: [PATCH 16/30] fixup! fixup! fixes Signed-off-by: Oleksii Kurinnyi --- .../workspace/devworkspace_controller.go | 49 +++++++++++++++++- .../init_container_validation_test.go | 47 +++++++++++++++++ pkg/library/home/custom_init_test.go | 16 +++--- pkg/library/initcontainers/merge_test.go | 51 +++++++++++++++---- test/e2e/pkg/client/oc.go | 21 +++++--- 5 files changed, 156 insertions(+), 28 deletions(-) diff --git a/controllers/workspace/devworkspace_controller.go b/controllers/workspace/devworkspace_controller.go index d123ccd6a..f3f1ce525 100644 --- a/controllers/workspace/devworkspace_controller.go +++ b/controllers/workspace/devworkspace_controller.go @@ -18,6 +18,7 @@ package controllers import ( "context" "fmt" + "regexp" "strconv" "strings" "time" @@ -127,8 +128,8 @@ func validateNoAdvancedFields(c corev1.Container) error { // validateHomeInitContainer validates all aspects of the init-persistent-home container. func validateHomeInitContainer(c corev1.Container) error { - if strings.ContainsAny(c.Image, "\n\r\t ") { - return fmt.Errorf("invalid image reference for %s: image reference contains invalid whitespace characters", constants.HomeInitComponentName) + if err := validateImageReference(c.Image); err != nil { + return fmt.Errorf("invalid image reference for %s: %w", constants.HomeInitComponentName, err) } if len(c.Command) != 2 || c.Command[0] != "/bin/sh" || c.Command[1] != "-c" { @@ -150,6 +151,50 @@ func validateHomeInitContainer(c corev1.Container) error { return nil } +func validateImageReference(image string) error { + if image == "" { + return fmt.Errorf("image reference cannot be empty") + } + + // whitespace and control characters + if strings.ContainsAny(image, "\n\r\t ") { + return fmt.Errorf("contains invalid whitespace characters") + } + + // other control characters + for _, r := range image { + if r < 0x20 || r == 0x7F { + return fmt.Errorf("contains invalid control characters") + } + } + + // format: [registry[:port]/]repository[:tag][@digest] + imagePattern := regexp.MustCompile(`^([a-zA-Z0-9]([a-zA-Z0-9._-]*[a-zA-Z0-9])*|\[?[0-9a-fA-F:]+]?)(:\d{1,5})?(/[a-zA-Z0-9]([a-zA-Z0-9._/-]*[a-zA-Z0-9])*)*(:[a-zA-Z0-9_.-]+)?(@sha256:[a-f0-9]{64})?$`) + if !imagePattern.MatchString(image) { + return fmt.Errorf("invalid format: should match regex: %s", imagePattern.String()) + } + + // port range + portMatch := regexp.MustCompile(`:(\d{1,5})(/|:|@|$)`) + matches := portMatch.FindStringSubmatch(image) + if len(matches) > 1 { + port, err := strconv.Atoi(matches[1]) + if err != nil { + return fmt.Errorf("invalid port format: %w", err) + } + if port < 1 || port > 65535 { + return fmt.Errorf("invalid port number: %d (must be 1-65535)", port) + } + } + + // length check + if len(image) > 4096 { + return fmt.Errorf("length exceeds 4096 characters") + } + + return nil +} + // ensureHomeInitContainerFields ensures that an init-persistent-home container has // the correct Command, Args, and VolumeMounts. func ensureHomeInitContainerFields(c *corev1.Container) error { diff --git a/controllers/workspace/init_container_validation_test.go b/controllers/workspace/init_container_validation_test.go index 8610e6ea6..2541333f0 100644 --- a/controllers/workspace/init_container_validation_test.go +++ b/controllers/workspace/init_container_validation_test.go @@ -16,6 +16,7 @@ package controllers import ( + "strings" "testing" dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" @@ -301,3 +302,49 @@ func TestDefaultAndValidateHomeInitContainer_NoWorkspaceImage(t *testing.T) { assert.Error(t, err) assert.Contains(t, err.Error(), "unable to infer workspace image") } + +func TestValidateImageReference(t *testing.T) { + tests := []struct { + name string + image string + expectError bool + errorMsg string + }{ + // Valid images + {"simple image", "nginx", false, ""}, + {"image with tag", "nginx:latest", false, ""}, + {"image with version tag", "nginx:1.21", false, ""}, + {"registry image", "docker.io/nginx", false, ""}, + {"registry with tag", "docker.io/nginx:latest", false, ""}, + {"registry with port", "localhost:5000/nginx", false, ""}, + {"registry port with tag", "localhost:5000/nginx:latest", false, ""}, + {"multi-level path", "registry.example.com/team/project/app", false, ""}, + {"with digest", "nginx@sha256:abc123def4567890abcdef1234567890abcdef1234567890abcdef1234567890", false, ""}, + {"full reference", "registry.example.com:8080/team/app:v1.2.3@sha256:abc123def4567890abcdef1234567890abcdef1234567890abcdef1234567890", false, ""}, + + // Invalid images + {"empty image", "", true, "cannot be empty"}, + {"whitespace", "nginx latest", true, "whitespace"}, + {"newline", "nginx\nlatest", true, "whitespace"}, + {"tab", "nginx\tlatest", true, "whitespace"}, + {"control char", "nginx\x00latest", true, "control characters"}, + {"invalid port 0", "registry:0/image", true, "port number"}, + {"invalid port 65536", "registry:65536/image", true, "port number"}, + {"invalid format", "-nginx", true, "invalid format: should match regex: ^([a-zA-Z0-9]([a-zA-Z0-9._-]*[a-zA-Z0-9])*|\\[?[0-9a-fA-F:]+]?)(:\\d{1,5})?(/[a-zA-Z0-9]([a-zA-Z0-9._/-]*[a-zA-Z0-9])*)*(:[a-zA-Z0-9_.-]+)?(@sha256:[a-f0-9]{64})?$"}, + {"too long", strings.Repeat("a", 4097), true, "exceeds 4096"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateImageReference(tt.image) + if tt.expectError { + assert.Error(t, err) + if tt.errorMsg != "" { + assert.Contains(t, err.Error(), tt.errorMsg) + } + } else { + assert.NoError(t, err, "Image %q should be valid", tt.image) + } + }) + } +} diff --git a/pkg/library/home/custom_init_test.go b/pkg/library/home/custom_init_test.go index 6ecb0f34f..003d9cfbe 100644 --- a/pkg/library/home/custom_init_test.go +++ b/pkg/library/home/custom_init_test.go @@ -19,13 +19,13 @@ import ( "testing" dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" - attributes "github.com/devfile/api/v2/pkg/attributes" + "github.com/devfile/api/v2/pkg/attributes" "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1" "github.com/devfile/devworkspace-operator/pkg/common" "github.com/devfile/devworkspace-operator/pkg/constants" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" ) func TestCustomInitPersistentHome(t *testing.T) { @@ -61,7 +61,7 @@ func TestCustomInitPersistentHome(t *testing.T) { Config: &v1alpha1.OperatorConfiguration{ Workspace: &v1alpha1.WorkspaceConfig{ PersistUserHome: &v1alpha1.PersistentHomeConfig{ - Enabled: pointer.Bool(true), + Enabled: ptr.To(true), }, InitContainers: []corev1.Container{ { @@ -100,7 +100,7 @@ func TestCustomInitPersistentHome(t *testing.T) { Config: &v1alpha1.OperatorConfiguration{ Workspace: &v1alpha1.WorkspaceConfig{ PersistUserHome: &v1alpha1.PersistentHomeConfig{ - Enabled: pointer.Bool(true), + Enabled: ptr.To(true), }, InitContainers: []corev1.Container{ { @@ -140,8 +140,8 @@ func TestCustomInitPersistentHome(t *testing.T) { Config: &v1alpha1.OperatorConfiguration{ Workspace: &v1alpha1.WorkspaceConfig{ PersistUserHome: &v1alpha1.PersistentHomeConfig{ - Enabled: pointer.Bool(true), - DisableInitContainer: pointer.Bool(false), + Enabled: ptr.To(true), + DisableInitContainer: ptr.To(false), }, InitContainers: []corev1.Container{}, }, @@ -175,8 +175,8 @@ func TestCustomInitPersistentHome(t *testing.T) { Config: &v1alpha1.OperatorConfiguration{ Workspace: &v1alpha1.WorkspaceConfig{ PersistUserHome: &v1alpha1.PersistentHomeConfig{ - Enabled: pointer.Bool(true), - DisableInitContainer: pointer.Bool(true), + Enabled: ptr.To(true), + DisableInitContainer: ptr.To(true), }, }, }, diff --git a/pkg/library/initcontainers/merge_test.go b/pkg/library/initcontainers/merge_test.go index 825e8dc85..8afc62a02 100644 --- a/pkg/library/initcontainers/merge_test.go +++ b/pkg/library/initcontainers/merge_test.go @@ -28,7 +28,6 @@ func TestMergeInitContainers(t *testing.T) { base []corev1.Container patches []corev1.Container want []corev1.Container - wantErr bool }{ { name: "empty base", @@ -39,7 +38,6 @@ func TestMergeInitContainers(t *testing.T) { want: []corev1.Container{ {Name: "new-container", Image: "new-image"}, }, - wantErr: false, }, { name: "empty patches", @@ -50,7 +48,6 @@ func TestMergeInitContainers(t *testing.T) { want: []corev1.Container{ {Name: "base-container", Image: "base-image"}, }, - wantErr: false, }, { name: "multiple containers", @@ -69,7 +66,6 @@ func TestMergeInitContainers(t *testing.T) { {Name: "third", Image: "third-image"}, {Name: "new-container", Image: "new-image"}, }, - wantErr: false, }, { name: "partial field merge", @@ -97,19 +93,52 @@ func TestMergeInitContainers(t *testing.T) { Env: []corev1.EnvVar{{Name: "BASE_VAR", Value: "base-value"}}, }, }, - wantErr: false, + }, + { + name: "preserve user-configured init-persistent-home content", + base: []corev1.Container{ + { + Name: "init-persistent-home", + Image: "workspace-image:latest", + Command: []string{"/bin/sh", "-c"}, + Args: []string{"default stow script"}, + }, + }, + patches: []corev1.Container{ + { + Name: "init-persistent-home", + Image: "custom-image:latest", + Args: []string{"echo 'custom init'"}, + Env: []corev1.EnvVar{ + { + Name: "CUSTOM_VAR", + Value: "custom-value", + }, + }, + }, + }, + want: []corev1.Container{ + { + Name: "init-persistent-home", + Image: "custom-image:latest", + Command: []string{"/bin/sh", "-c"}, + Args: []string{"echo 'custom init'"}, + Env: []corev1.EnvVar{ + { + Name: "CUSTOM_VAR", + Value: "custom-value", + }, + }, + }, + }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := MergeInitContainers(tt.base, tt.patches) - if tt.wantErr { - assert.Error(t, err, "should return error") - } else { - assert.NoError(t, err, "should not return error") - assert.Equal(t, tt.want, got, "should return merged containers") - } + assert.NoError(t, err, "should not return error") + assert.Equal(t, tt.want, got, "should return merged containers") }) } } diff --git a/test/e2e/pkg/client/oc.go b/test/e2e/pkg/client/oc.go index 0d9e482a4..4b3e5f0c9 100644 --- a/test/e2e/pkg/client/oc.go +++ b/test/e2e/pkg/client/oc.go @@ -16,11 +16,15 @@ package client import ( + "context" "fmt" "log" "os/exec" "strings" "time" + + dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" + k8sErrors "k8s.io/apimachinery/pkg/api/errors" ) func (w *K8sClient) OcApplyWorkspace(namespace string, filePath string) (commandResult string, err error) { @@ -68,12 +72,15 @@ func (w *K8sClient) GetLogsForContainer(podName string, namespace, containerName return string(outBytes), err } +// function returns an empty string as `commandResult` to match the existing signature. func (w *K8sClient) OcDeleteWorkspace(name, namespace string) (commandResult string, err error) { - cmd := exec.Command("bash", "-c", fmt.Sprintf( - "KUBECONFIG=%s oc delete devworkspace %s -n %s --ignore-not-found=true", - w.kubeCfgFile, - name, - namespace)) - outBytes, err := cmd.CombinedOutput() - return string(outBytes), err + workspace := &dw.DevWorkspace{} + workspace.ObjectMeta.Name = name + workspace.ObjectMeta.Namespace = namespace + + err = w.crClient.Delete(context.TODO(), workspace) + if err != nil && !k8sErrors.IsNotFound(err) { + return "", err + } + return "", nil } From f82748ac7b65ba7e0646cfe16b81ca9143901329 Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Tue, 25 Nov 2025 15:54:00 +0200 Subject: [PATCH 17/30] fixup! fixup! fixup! fixes Signed-off-by: Oleksii Kurinnyi --- apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go | 1 - controllers/workspace/devworkspace_controller.go | 2 +- .../controller.devfile.io_devworkspaceoperatorconfigs.yaml | 1 - deploy/deployment/kubernetes/combined.yaml | 1 - ...rconfigs.controller.devfile.io.CustomResourceDefinition.yaml | 1 - deploy/deployment/openshift/combined.yaml | 1 - ...rconfigs.controller.devfile.io.CustomResourceDefinition.yaml | 1 - .../controller.devfile.io_devworkspaceoperatorconfigs.yaml | 1 - pkg/constants/constants.go | 2 ++ 9 files changed, 3 insertions(+), 8 deletions(-) diff --git a/apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go b/apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go index 829b888aa..809e3ec74 100644 --- a/apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go +++ b/apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go @@ -205,7 +205,6 @@ type WorkspaceConfig struct { HostUsers *bool `json:"hostUsers,omitempty"` // InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. // Typical uses: injecting organization tools/configs, initializing persistent home, etc. - // Note: Only administrators should be allowed to edit the DevWorkspaceOperatorConfig. InitContainers []corev1.Container `json:"initContainers,omitempty"` } diff --git a/controllers/workspace/devworkspace_controller.go b/controllers/workspace/devworkspace_controller.go index f3f1ce525..ffc808cd9 100644 --- a/controllers/workspace/devworkspace_controller.go +++ b/controllers/workspace/devworkspace_controller.go @@ -535,7 +535,7 @@ func (r *DevWorkspaceReconciler) Reconcile(ctx context.Context, req ctrl.Request // Inject operator-configured init containers if workspace.Config != nil && workspace.Config.Workspace != nil && len(workspace.Config.Workspace.InitContainers) > 0 { // Check if init-persistent-home should be disabled - disableHomeInit := pointer.BoolDeref(workspace.Config.Workspace.PersistUserHome.DisableInitContainer, false) + disableHomeInit := pointer.BoolDeref(workspace.Config.Workspace.PersistUserHome.DisableInitContainer, constants.DefaultDisableHomeInitContainer) // Prepare patches: filter and preprocess init containers from config patches := []corev1.Container{} diff --git a/deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml b/deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml index c4f476950..f140acf8f 100644 --- a/deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml +++ b/deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml @@ -2429,7 +2429,6 @@ spec: description: |- InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. Typical uses: injecting organization tools/configs, initializing persistent home, etc. - Note: Only administrators should be allowed to edit the DevWorkspaceOperatorConfig. items: description: A single application container that you want to run within a pod. properties: diff --git a/deploy/deployment/kubernetes/combined.yaml b/deploy/deployment/kubernetes/combined.yaml index d2053523e..f204ba566 100644 --- a/deploy/deployment/kubernetes/combined.yaml +++ b/deploy/deployment/kubernetes/combined.yaml @@ -2559,7 +2559,6 @@ spec: description: |- InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. Typical uses: injecting organization tools/configs, initializing persistent home, etc. - Note: Only administrators should be allowed to edit the DevWorkspaceOperatorConfig. items: description: A single application container that you want to run within a pod. diff --git a/deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml b/deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml index bca0eae00..f30dadd1f 100644 --- a/deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml +++ b/deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml @@ -2559,7 +2559,6 @@ spec: description: |- InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. Typical uses: injecting organization tools/configs, initializing persistent home, etc. - Note: Only administrators should be allowed to edit the DevWorkspaceOperatorConfig. items: description: A single application container that you want to run within a pod. diff --git a/deploy/deployment/openshift/combined.yaml b/deploy/deployment/openshift/combined.yaml index e560caa99..13caa3fbd 100644 --- a/deploy/deployment/openshift/combined.yaml +++ b/deploy/deployment/openshift/combined.yaml @@ -2559,7 +2559,6 @@ spec: description: |- InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. Typical uses: injecting organization tools/configs, initializing persistent home, etc. - Note: Only administrators should be allowed to edit the DevWorkspaceOperatorConfig. items: description: A single application container that you want to run within a pod. diff --git a/deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml b/deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml index bca0eae00..f30dadd1f 100644 --- a/deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml +++ b/deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml @@ -2559,7 +2559,6 @@ spec: description: |- InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. Typical uses: injecting organization tools/configs, initializing persistent home, etc. - Note: Only administrators should be allowed to edit the DevWorkspaceOperatorConfig. items: description: A single application container that you want to run within a pod. diff --git a/deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml b/deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml index f42c03b3e..7efa4df2c 100644 --- a/deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml +++ b/deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml @@ -2557,7 +2557,6 @@ spec: description: |- InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. Typical uses: injecting organization tools/configs, initializing persistent home, etc. - Note: Only administrators should be allowed to edit the DevWorkspaceOperatorConfig. items: description: A single application container that you want to run within a pod. diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index b63830659..202354c44 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -45,6 +45,8 @@ const ( HomeInitEventId = "init-persistent-home" + DefaultDisableHomeInitContainer = false + SshAgentStartEventId = "init-ssh-agent-command" ServiceAccount = "devworkspace" From 686e07b4de03dcb73fc297fda8cfb69939b1d58c Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Tue, 25 Nov 2025 17:52:30 +0200 Subject: [PATCH 18/30] fixup! fixup! fixup! fixup! fixes Signed-off-by: Oleksii Kurinnyi --- test/resources/custom-init-test-workspace.yaml | 5 ++--- test/resources/disabled-init-test-workspace.yaml | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/test/resources/custom-init-test-workspace.yaml b/test/resources/custom-init-test-workspace.yaml index 948f684d9..4f665434a 100644 --- a/test/resources/custom-init-test-workspace.yaml +++ b/test/resources/custom-init-test-workspace.yaml @@ -11,6 +11,5 @@ spec: components: - name: tooling container: - image: quay.io/devfile/universal-developer-image:latest - memoryLimit: 2Gi - mountSources: true + image: quay.io/wto/web-terminal-tooling:latest + args: ["tail", "-f", "/dev/null"] diff --git a/test/resources/disabled-init-test-workspace.yaml b/test/resources/disabled-init-test-workspace.yaml index af3a26e46..503de73b4 100644 --- a/test/resources/disabled-init-test-workspace.yaml +++ b/test/resources/disabled-init-test-workspace.yaml @@ -11,6 +11,5 @@ spec: components: - name: tooling container: - image: quay.io/devfile/universal-developer-image:latest - memoryLimit: 2Gi - mountSources: true + image: quay.io/wto/web-terminal-tooling:latest + args: ["tail", "-f", "/dev/null"] From 111086524cebbf31f737ec73989132c75c2969be Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Fri, 28 Nov 2025 16:42:20 +0200 Subject: [PATCH 19/30] fixup! fixup! fixup! fixup! fixup! fixes Signed-off-by: Oleksii Kurinnyi --- test/e2e/pkg/tests/custom_init_container_tests.go | 10 +++++----- test/resources/dwoc-custom-init.yaml | 1 - test/resources/dwoc-disabled-init.yaml | 1 - 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/test/e2e/pkg/tests/custom_init_container_tests.go b/test/e2e/pkg/tests/custom_init_container_tests.go index 43a127228..b55c224c6 100644 --- a/test/e2e/pkg/tests/custom_init_container_tests.go +++ b/test/e2e/pkg/tests/custom_init_container_tests.go @@ -57,10 +57,10 @@ var _ = ginkgo.Describe("[Custom Init Container Tests]", func() { ginkgo.BeforeEach(func() { dwocFile := filepath.Join(getProjectRoot(), "test", "resources", "dwoc-custom-init.yaml") - cmd := exec.Command("kubectl", "apply", "-f", dwocFile) + cmd := exec.Command("kubectl", "apply", "-f", dwocFile, "-n", config.OperatorNamespace) output, err := cmd.CombinedOutput() if err != nil { - ginkgo.Fail(fmt.Sprintf("Failed to apply DWOC: %s. Output: %s", err, string(output))) + ginkgo.Fail(fmt.Sprintf("Failed to apply DWOC to namespace %s: %s. Output: %s", config.OperatorNamespace, err, string(output))) } }) @@ -78,7 +78,7 @@ var _ = ginkgo.Describe("[Custom Init Container Tests]", func() { ginkgo.Fail(fmt.Sprintf("Workspace didn't start properly. Error: %s", err)) } - // Wait for pod to be running (may take a while for large image pulls) + // Wait for pod to be running podSelector := fmt.Sprintf("controller.devfile.io/devworkspace_name=%s", workspaceName) var podName string gomega.Eventually(func() error { @@ -105,10 +105,10 @@ var _ = ginkgo.Describe("[Custom Init Container Tests]", func() { ginkgo.BeforeEach(func() { dwocFile := filepath.Join(getProjectRoot(), "test", "resources", "dwoc-disabled-init.yaml") - cmd := exec.Command("kubectl", "apply", "-f", dwocFile) + cmd := exec.Command("kubectl", "apply", "-f", dwocFile, "-n", config.OperatorNamespace) output, err := cmd.CombinedOutput() if err != nil { - ginkgo.Fail(fmt.Sprintf("Failed to apply DWOC: %s. Output: %s", err, string(output))) + ginkgo.Fail(fmt.Sprintf("Failed to apply DWOC to namespace %s: %s. Output: %s", config.OperatorNamespace, err, string(output))) } }) diff --git a/test/resources/dwoc-custom-init.yaml b/test/resources/dwoc-custom-init.yaml index f8e716e55..beb65cb65 100644 --- a/test/resources/dwoc-custom-init.yaml +++ b/test/resources/dwoc-custom-init.yaml @@ -2,7 +2,6 @@ apiVersion: controller.devfile.io/v1alpha1 kind: DevWorkspaceOperatorConfig metadata: name: devworkspace-operator-config - namespace: openshift-operators config: workspace: initContainers: diff --git a/test/resources/dwoc-disabled-init.yaml b/test/resources/dwoc-disabled-init.yaml index b8bebd6e3..b6b33ac92 100644 --- a/test/resources/dwoc-disabled-init.yaml +++ b/test/resources/dwoc-disabled-init.yaml @@ -2,7 +2,6 @@ apiVersion: controller.devfile.io/v1alpha1 kind: DevWorkspaceOperatorConfig metadata: name: devworkspace-operator-config - namespace: openshift-operators config: workspace: persistUserHome: From 8aaac10be00d370a5db351462f889a5f8207256f Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Tue, 2 Dec 2025 17:43:54 +0200 Subject: [PATCH 20/30] fixup! fixup! fixup! fixup! fixup! fixup! fixes Signed-off-by: Oleksii Kurinnyi --- .../workspace/devworkspace_controller.go | 68 +++------ .../init_container_validation_test.go | 135 ++++-------------- pkg/library/home/persistentHome.go | 13 +- 3 files changed, 49 insertions(+), 167 deletions(-) diff --git a/controllers/workspace/devworkspace_controller.go b/controllers/workspace/devworkspace_controller.go index ffc808cd9..4b0c3bed8 100644 --- a/controllers/workspace/devworkspace_controller.go +++ b/controllers/workspace/devworkspace_controller.go @@ -69,22 +69,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" ) -// applyHomeInitDefaults applies default values for image and command fields -// of the init-persistent-home container. -func applyHomeInitDefaults(c corev1.Container, workspace *common.DevWorkspaceWithConfig) (corev1.Container, error) { - if c.Image == "" { - inferred := home.InferWorkspaceImage(&workspace.Spec.Template) - if inferred == "" { - return c, fmt.Errorf("unable to infer workspace image for %s; specify image explicitly", constants.HomeInitComponentName) - } - c.Image = inferred - } - if len(c.Command) == 0 { - c.Command = []string{"/bin/sh", "-c"} - } - return c, nil -} - // validateNoAdvancedFields validates that the init-persistent-home container // does not use advanced Kubernetes container fields that could make behavior unpredictable. func validateNoAdvancedFields(c corev1.Container) error { @@ -127,23 +111,36 @@ func validateNoAdvancedFields(c corev1.Container) error { } // validateHomeInitContainer validates all aspects of the init-persistent-home container. +// It only validates fields that are present, as missing fields will be filled by +// the strategic merge with the default init container. func validateHomeInitContainer(c corev1.Container) error { - if err := validateImageReference(c.Image); err != nil { - return fmt.Errorf("invalid image reference for %s: %w", constants.HomeInitComponentName, err) + // Only validate if present + if c.Image != "" { + if err := validateImageReference(c.Image); err != nil { + return fmt.Errorf("invalid image reference for %s: %w", constants.HomeInitComponentName, err) + } } - if len(c.Command) != 2 || c.Command[0] != "/bin/sh" || c.Command[1] != "-c" { - return fmt.Errorf("command must be exactly [/bin/sh, -c] for %s", constants.HomeInitComponentName) + // Only validate if present + if c.Command != nil { + if len(c.Command) != 2 || c.Command[0] != "/bin/sh" || c.Command[1] != "-c" { + return fmt.Errorf("command must be exactly [/bin/sh, -c] for %s", constants.HomeInitComponentName) + } } - if len(c.Args) != 1 { - return fmt.Errorf("args must contain exactly one script string for %s", constants.HomeInitComponentName) + // Only validate if present + if c.Args != nil { + if len(c.Args) != 1 { + return fmt.Errorf("args must contain exactly one script string for %s", constants.HomeInitComponentName) + } } + // Always validate - should not be provided if len(c.VolumeMounts) > 0 { return fmt.Errorf("volumeMounts are not allowed for %s; persistent-home is auto-mounted at /home/user/", constants.HomeInitComponentName) } + // Always validate - should not be provided if err := validateNoAdvancedFields(c); err != nil { return err } @@ -209,28 +206,6 @@ func ensureHomeInitContainerFields(c *corev1.Container) error { return nil } -// defaultAndValidateHomeInitContainer applies defaults and validation for a custom -// DWOC-provided init container named init-persistent-home. It ensures a shell execution -// model, a single script arg, injects the persistent-home mount at /home/user/, and -// defaults image to the inferred workspace image if not provided. -func defaultAndValidateHomeInitContainer(c corev1.Container, workspace *common.DevWorkspaceWithConfig) (corev1.Container, error) { - var err error - - if c, err = applyHomeInitDefaults(c, workspace); err != nil { - return c, err - } - - if err = validateHomeInitContainer(c); err != nil { - return c, err - } - - if err = ensureHomeInitContainerFields(&c); err != nil { - return c, err - } - - return c, nil -} - const ( startingWorkspaceRequeueInterval = 5 * time.Second ) @@ -552,9 +527,8 @@ func (r *DevWorkspaceReconciler) Reconcile(ctx context.Context, req ctrl.Request reqLogger.Info("Skipping init-persistent-home container: DisableInitContainer is true") continue } - // Apply defaults and validation for init-persistent-home - container, err = defaultAndValidateHomeInitContainer(container, workspace) - if err != nil { + // Validate custom home init container + if err := validateHomeInitContainer(container); err != nil { return r.failWorkspace(workspace, fmt.Sprintf("Invalid %s container: %s", constants.HomeInitComponentName, err), metrics.ReasonBadRequest, reqLogger, &reconcileStatus), nil } } diff --git a/controllers/workspace/init_container_validation_test.go b/controllers/workspace/init_container_validation_test.go index 2541333f0..c2c4373ad 100644 --- a/controllers/workspace/init_container_validation_test.go +++ b/controllers/workspace/init_container_validation_test.go @@ -19,71 +19,34 @@ import ( "strings" "testing" - dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" - "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1" - "github.com/devfile/devworkspace-operator/pkg/common" "github.com/devfile/devworkspace-operator/pkg/constants" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" ) -func TestDefaultAndValidateHomeInitContainer(t *testing.T) { - workspace := &common.DevWorkspaceWithConfig{ - DevWorkspace: &dw.DevWorkspace{ - Spec: dw.DevWorkspaceSpec{ - Template: dw.DevWorkspaceTemplateSpec{ - DevWorkspaceTemplateSpecContent: dw.DevWorkspaceTemplateSpecContent{ - Components: []dw.Component{ - { - Name: "main-container", - ComponentUnion: dw.ComponentUnion{ - Container: &dw.ContainerComponent{ - Container: dw.Container{ - Image: "test-image:latest", - }, - }, - }, - }, - }, - }, - }, - }, - }, - Config: &v1alpha1.OperatorConfiguration{ - Workspace: &v1alpha1.WorkspaceConfig{}, - }, - } - +func TestValidateInitContainer(t *testing.T) { tests := []struct { name string container corev1.Container expectError bool errorMsg string - validate func(t *testing.T, result corev1.Container) }{ { - name: "Defaults image when empty", + name: "Accepts container with only args (image and command will be filled by merge)", container: corev1.Container{ Name: constants.HomeInitComponentName, Args: []string{"echo 'test'"}, }, expectError: false, - validate: func(t *testing.T, result corev1.Container) { - assert.Equal(t, "test-image:latest", result.Image) - }, }, { - name: "Defaults command when empty", + name: "Accepts container with only image (command and args will be filled by merge)", container: corev1.Container{ Name: constants.HomeInitComponentName, Image: "custom-image:latest", - Args: []string{"echo 'test'"}, }, expectError: false, - validate: func(t *testing.T, result corev1.Container) { - assert.Equal(t, []string{"/bin/sh", "-c"}, result.Command) - }, }, { name: "Accepts valid command", @@ -100,7 +63,18 @@ func TestDefaultAndValidateHomeInitContainer(t *testing.T) { container: corev1.Container{ Name: constants.HomeInitComponentName, Image: "custom-image:latest", - Command: []string{"/bin/bash"}, + Command: []string{"/bin/sh"}, + Args: []string{"echo 'test'"}, + }, + expectError: true, + errorMsg: "command must be exactly [/bin/sh, -c]", + }, + { + name: "Rejects empty command", + container: corev1.Container{ + Name: constants.HomeInitComponentName, + Image: "custom-image:latest", + Command: []string{}, Args: []string{"echo 'test'"}, }, expectError: true, @@ -131,7 +105,6 @@ func TestDefaultAndValidateHomeInitContainer(t *testing.T) { container: corev1.Container{ Name: constants.HomeInitComponentName, Image: "custom-image:latest", - Args: []string{"echo 'test'"}, VolumeMounts: []corev1.VolumeMount{ { Name: "custom-volume", @@ -142,44 +115,29 @@ func TestDefaultAndValidateHomeInitContainer(t *testing.T) { expectError: true, errorMsg: "volumeMounts are not allowed for init-persistent-home", }, - { - name: "Injects persistent-home volumeMount", - container: corev1.Container{ - Name: constants.HomeInitComponentName, - Image: "custom-image:latest", - Args: []string{"echo 'test'"}, - }, - expectError: false, - validate: func(t *testing.T, result corev1.Container) { - assert.Len(t, result.VolumeMounts, 1) - assert.Equal(t, constants.HomeVolumeName, result.VolumeMounts[0].Name) - assert.Equal(t, constants.HomeUserDirectory, result.VolumeMounts[0].MountPath) - }, - }, { name: "Allows env variables", container: corev1.Container{ Name: constants.HomeInitComponentName, Image: "custom-image:latest", - Args: []string{"echo 'test'"}, Env: []corev1.EnvVar{ - {Name: "TEST_VAR", Value: "test-value"}, + { + Name: "TEST_VAR", + Value: "test-var", + }, }, }, expectError: false, - validate: func(t *testing.T, result corev1.Container) { - assert.Len(t, result.Env, 1) - assert.Equal(t, "TEST_VAR", result.Env[0].Name) - }, }, { name: "Rejects ports", container: corev1.Container{ Name: constants.HomeInitComponentName, Image: "custom-image:latest", - Args: []string{"echo 'test'"}, Ports: []corev1.ContainerPort{ - {ContainerPort: 8080}, + { + ContainerPort: 8080, + }, }, }, expectError: true, @@ -190,10 +148,11 @@ func TestDefaultAndValidateHomeInitContainer(t *testing.T) { container: corev1.Container{ Name: constants.HomeInitComponentName, Image: "custom-image:latest", - Args: []string{"echo 'test'"}, LivenessProbe: &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ - HTTPGet: &corev1.HTTPGetAction{Path: "/health"}, + HTTPGet: &corev1.HTTPGetAction{ + Path: "/health", + }, }, }, }, @@ -205,7 +164,6 @@ func TestDefaultAndValidateHomeInitContainer(t *testing.T) { container: corev1.Container{ Name: constants.HomeInitComponentName, Image: "custom-image:latest", - Args: []string{"echo 'test'"}, SecurityContext: &corev1.SecurityContext{ RunAsUser: new(int64), }, @@ -218,7 +176,6 @@ func TestDefaultAndValidateHomeInitContainer(t *testing.T) { container: corev1.Container{ Name: constants.HomeInitComponentName, Image: "custom-image:latest", - Args: []string{"echo 'test'"}, Resources: corev1.ResourceRequirements{ Limits: corev1.ResourceList{ corev1.ResourceMemory: resource.MustParse("128Mi"), @@ -233,7 +190,6 @@ func TestDefaultAndValidateHomeInitContainer(t *testing.T) { container: corev1.Container{ Name: constants.HomeInitComponentName, Image: "custom-image:latest", - Args: []string{"echo 'test'"}, WorkingDir: "/tmp", }, expectError: true, @@ -243,8 +199,7 @@ func TestDefaultAndValidateHomeInitContainer(t *testing.T) { name: "Rejects image with whitespace", container: corev1.Container{ Name: constants.HomeInitComponentName, - Image: "nginx\nmalicious", - Args: []string{"echo 'test'"}, + Image: "nginx\tmalicious", }, expectError: true, errorMsg: "invalid image reference", @@ -253,7 +208,7 @@ func TestDefaultAndValidateHomeInitContainer(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result, err := defaultAndValidateHomeInitContainer(tt.container, workspace) + err := validateHomeInitContainer(tt.container) if tt.expectError { assert.Error(t, err) @@ -262,47 +217,11 @@ func TestDefaultAndValidateHomeInitContainer(t *testing.T) { } } else { assert.NoError(t, err) - if tt.validate != nil { - tt.validate(t, result) - } } }) } } -func TestDefaultAndValidateHomeInitContainer_NoWorkspaceImage(t *testing.T) { - workspaceNoImage := &common.DevWorkspaceWithConfig{ - DevWorkspace: &dw.DevWorkspace{ - Spec: dw.DevWorkspaceSpec{ - Template: dw.DevWorkspaceTemplateSpec{ - DevWorkspaceTemplateSpecContent: dw.DevWorkspaceTemplateSpecContent{ - Components: []dw.Component{ - { - Name: "volume-component", - ComponentUnion: dw.ComponentUnion{ - Volume: &dw.VolumeComponent{}, - }, - }, - }, - }, - }, - }, - }, - Config: &v1alpha1.OperatorConfiguration{ - Workspace: &v1alpha1.WorkspaceConfig{}, - }, - } - - container := corev1.Container{ - Name: constants.HomeInitComponentName, - Args: []string{"echo 'test'"}, - } - - _, err := defaultAndValidateHomeInitContainer(container, workspaceNoImage) - assert.Error(t, err) - assert.Contains(t, err.Error(), "unable to infer workspace image") -} - func TestValidateImageReference(t *testing.T) { tests := []struct { name string diff --git a/pkg/library/home/persistentHome.go b/pkg/library/home/persistentHome.go index c6e0a9cc8..3ab75ca52 100644 --- a/pkg/library/home/persistentHome.go +++ b/pkg/library/home/persistentHome.go @@ -62,19 +62,8 @@ func AddPersistentHomeVolume(workspace *common.DevWorkspaceWithConfig) (*v1alpha Path: constants.HomeUserDirectory, } - // Determine if a custom home init container is configured via DWOC - hasCustomHomeInit := false - if workspace.Config != nil && workspace.Config.Workspace != nil { - for _, c := range workspace.Config.Workspace.InitContainers { - if c.Name == constants.HomeInitComponentName { - hasCustomHomeInit = true - break - } - } - } - // Add default init container only if not disabled and no custom init is configured - if (workspace.Config.Workspace.PersistUserHome.DisableInitContainer == nil || !*workspace.Config.Workspace.PersistUserHome.DisableInitContainer) && !hasCustomHomeInit { + if workspace.Config.Workspace.PersistUserHome.DisableInitContainer == nil || !*workspace.Config.Workspace.PersistUserHome.DisableInitContainer { err := addInitContainer(dwTemplateSpecCopy) if err != nil { return nil, fmt.Errorf("failed to add init container for home persistence setup: %w", err) From 665662c7968043c15e8699ad213a3483310dfb39 Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Tue, 2 Dec 2025 17:44:19 +0200 Subject: [PATCH 21/30] Apply suggestion from @dkwon17 Co-authored-by: David Kwon Signed-off-by: Oleksii Kurinnyi --- apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go b/apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go index 809e3ec74..5e2e6398b 100644 --- a/apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go +++ b/apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go @@ -204,7 +204,7 @@ type WorkspaceConfig struct { // +kubebuilder:validation:Optional HostUsers *bool `json:"hostUsers,omitempty"` // InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. - // Typical uses: injecting organization tools/configs, initializing persistent home, etc. + // Typical uses cases include injecting organization tools/configs, initializing persistent home, etc. InitContainers []corev1.Container `json:"initContainers,omitempty"` } From 444244a65a26dbd7098e8432a71fdb26e330e36d Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Tue, 2 Dec 2025 17:58:02 +0200 Subject: [PATCH 22/30] fixup! Apply suggestion from @dkwon17 Signed-off-by: Oleksii Kurinnyi --- .../controller.devfile.io_devworkspaceoperatorconfigs.yaml | 2 +- deploy/deployment/kubernetes/combined.yaml | 2 +- ...rconfigs.controller.devfile.io.CustomResourceDefinition.yaml | 2 +- deploy/deployment/openshift/combined.yaml | 2 +- ...rconfigs.controller.devfile.io.CustomResourceDefinition.yaml | 2 +- .../controller.devfile.io_devworkspaceoperatorconfigs.yaml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml b/deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml index f140acf8f..c9d94f683 100644 --- a/deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml +++ b/deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml @@ -2428,7 +2428,7 @@ spec: initContainers: description: |- InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. - Typical uses: injecting organization tools/configs, initializing persistent home, etc. + Typical uses cases include injecting organization tools/configs, initializing persistent home, etc. items: description: A single application container that you want to run within a pod. properties: diff --git a/deploy/deployment/kubernetes/combined.yaml b/deploy/deployment/kubernetes/combined.yaml index f204ba566..dd4348a68 100644 --- a/deploy/deployment/kubernetes/combined.yaml +++ b/deploy/deployment/kubernetes/combined.yaml @@ -2558,7 +2558,7 @@ spec: initContainers: description: |- InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. - Typical uses: injecting organization tools/configs, initializing persistent home, etc. + Typical uses cases include injecting organization tools/configs, initializing persistent home, etc. items: description: A single application container that you want to run within a pod. diff --git a/deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml b/deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml index f30dadd1f..d5e44c3c9 100644 --- a/deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml +++ b/deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml @@ -2558,7 +2558,7 @@ spec: initContainers: description: |- InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. - Typical uses: injecting organization tools/configs, initializing persistent home, etc. + Typical uses cases include injecting organization tools/configs, initializing persistent home, etc. items: description: A single application container that you want to run within a pod. diff --git a/deploy/deployment/openshift/combined.yaml b/deploy/deployment/openshift/combined.yaml index 13caa3fbd..ee533df65 100644 --- a/deploy/deployment/openshift/combined.yaml +++ b/deploy/deployment/openshift/combined.yaml @@ -2558,7 +2558,7 @@ spec: initContainers: description: |- InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. - Typical uses: injecting organization tools/configs, initializing persistent home, etc. + Typical uses cases include injecting organization tools/configs, initializing persistent home, etc. items: description: A single application container that you want to run within a pod. diff --git a/deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml b/deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml index f30dadd1f..d5e44c3c9 100644 --- a/deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml +++ b/deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml @@ -2558,7 +2558,7 @@ spec: initContainers: description: |- InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. - Typical uses: injecting organization tools/configs, initializing persistent home, etc. + Typical uses cases include injecting organization tools/configs, initializing persistent home, etc. items: description: A single application container that you want to run within a pod. diff --git a/deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml b/deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml index 7efa4df2c..734c97a9a 100644 --- a/deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml +++ b/deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml @@ -2556,7 +2556,7 @@ spec: initContainers: description: |- InitContainers defines a list of Kubernetes init containers that are automatically injected into all workspace pods. - Typical uses: injecting organization tools/configs, initializing persistent home, etc. + Typical uses cases include injecting organization tools/configs, initializing persistent home, etc. items: description: A single application container that you want to run within a pod. From 7c5c545a29de7b4657b4f10877f5c09409c36900 Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Wed, 3 Dec 2025 12:56:11 +0200 Subject: [PATCH 23/30] fixup! fixup! Apply suggestion from @dkwon17 Signed-off-by: Oleksii Kurinnyi --- pkg/library/home/custom_init_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/library/home/custom_init_test.go b/pkg/library/home/custom_init_test.go index 003d9cfbe..e79306569 100644 --- a/pkg/library/home/custom_init_test.go +++ b/pkg/library/home/custom_init_test.go @@ -36,7 +36,7 @@ func TestCustomInitPersistentHome(t *testing.T) { expectCustomInitSkipped bool }{ { - name: "Skips default init when custom init-persistent-home is provided", + name: "Adds default init when custom init-persistent-home is provided", workspace: &common.DevWorkspaceWithConfig{ DevWorkspace: &dw.DevWorkspace{ Spec: dw.DevWorkspaceSpec{ @@ -72,7 +72,7 @@ func TestCustomInitPersistentHome(t *testing.T) { }, }, }, - expectDefaultInitAdded: false, + expectDefaultInitAdded: true, }, { name: "Adds default init when no custom init-persistent-home is provided", From 2aa87082068d62c3fbcb983ded0295d4a28d27c8 Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Tue, 9 Dec 2025 14:33:46 +0200 Subject: [PATCH 24/30] fix Signed-off-by: Oleksii Kurinnyi --- test/e2e/pkg/client/devws.go | 15 +++++++++++++++ test/e2e/pkg/client/oc.go | 17 ----------------- .../pkg/tests/custom_init_container_tests.go | 4 ++-- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/test/e2e/pkg/client/devws.go b/test/e2e/pkg/client/devws.go index ca8859b55..ab7d48020 100644 --- a/test/e2e/pkg/client/devws.go +++ b/test/e2e/pkg/client/devws.go @@ -25,6 +25,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" + k8sErrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" ) @@ -90,3 +91,17 @@ func (w *K8sClient) WaitDevWsStatus(name, namespace string, expectedStatus dw.De } } } + +// DeleteDevWorkspace deletes a DevWorkspace using the Kubernetes client. +// Returns an error if the deletion fails (ignoring NotFound errors). +func (w *K8sClient) DeleteDevWorkspace(name, namespace string) error { + workspace := &dw.DevWorkspace{} + workspace.ObjectMeta.Name = name + workspace.ObjectMeta.Namespace = namespace + + err := w.crClient.Delete(context.TODO(), workspace) + if err != nil && !k8sErrors.IsNotFound(err) { + return err + } + return nil +} diff --git a/test/e2e/pkg/client/oc.go b/test/e2e/pkg/client/oc.go index 4b3e5f0c9..419d99283 100644 --- a/test/e2e/pkg/client/oc.go +++ b/test/e2e/pkg/client/oc.go @@ -16,15 +16,11 @@ package client import ( - "context" "fmt" "log" "os/exec" "strings" "time" - - dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" - k8sErrors "k8s.io/apimachinery/pkg/api/errors" ) func (w *K8sClient) OcApplyWorkspace(namespace string, filePath string) (commandResult string, err error) { @@ -71,16 +67,3 @@ func (w *K8sClient) GetLogsForContainer(podName string, namespace, containerName outBytes, err := cmd.CombinedOutput() return string(outBytes), err } - -// function returns an empty string as `commandResult` to match the existing signature. -func (w *K8sClient) OcDeleteWorkspace(name, namespace string) (commandResult string, err error) { - workspace := &dw.DevWorkspace{} - workspace.ObjectMeta.Name = name - workspace.ObjectMeta.Namespace = namespace - - err = w.crClient.Delete(context.TODO(), workspace) - if err != nil && !k8sErrors.IsNotFound(err) { - return "", err - } - return "", nil -} diff --git a/test/e2e/pkg/tests/custom_init_container_tests.go b/test/e2e/pkg/tests/custom_init_container_tests.go index b55c224c6..8f18d0a40 100644 --- a/test/e2e/pkg/tests/custom_init_container_tests.go +++ b/test/e2e/pkg/tests/custom_init_container_tests.go @@ -96,7 +96,7 @@ var _ = ginkgo.Describe("[Custom Init Container Tests]", func() { ginkgo.AfterEach(func() { // Cleanup workspace - _, _ = config.DevK8sClient.OcDeleteWorkspace(workspaceName, config.DevWorkspaceNamespace) + _ = config.DevK8sClient.DeleteDevWorkspace(workspaceName, config.DevWorkspaceNamespace) }) }) @@ -150,7 +150,7 @@ var _ = ginkgo.Describe("[Custom Init Container Tests]", func() { ginkgo.AfterEach(func() { // Cleanup workspace - _, _ = config.DevK8sClient.OcDeleteWorkspace(workspaceName, config.DevWorkspaceNamespace) + _ = config.DevK8sClient.DeleteDevWorkspace(workspaceName, config.DevWorkspaceNamespace) }) }) }) From 09c5bdc5665d9059e24e396203696dc912e42309 Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Tue, 9 Dec 2025 15:23:44 +0200 Subject: [PATCH 25/30] fixup! fix Signed-off-by: Oleksii Kurinnyi --- .../workspace/devworkspace_controller_test.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/controllers/workspace/devworkspace_controller_test.go b/controllers/workspace/devworkspace_controller_test.go index 89badecdb..9980a227e 100644 --- a/controllers/workspace/devworkspace_controller_test.go +++ b/controllers/workspace/devworkspace_controller_test.go @@ -150,6 +150,14 @@ var _ = Describe("DevWorkspace Controller", func() { return err == nil }, timeout, interval).Should(BeTrue(), "DevWorkspace should exist in cluster") + By("Waiting for the first DevWorkspace to have its ID set") + Eventually(func() (string, error) { + if err := k8sClient.Get(ctx, dwNamespacedName, createdDW); err != nil { + return "", err + } + return createdDW.Status.DevWorkspaceId, nil + }, timeout, interval).Should(Equal("test-workspace-id"), "First DevWorkspace should have ID set from override annotation") + By("Creating a DevWorkspace that duplicates the workspace ID of the first") Expect(k8sClient.Create(ctx, devworkspace2)).Should(Succeed()) defer deleteDevWorkspace(devworkspace2.Name) @@ -407,6 +415,13 @@ var _ = Describe("DevWorkspace Controller", func() { createObject(gitCredentials) defer deleteObject(gitCredentials) + By("Waiting for DevWorkspaceRouting to be created") + dwr := &controllerv1alpha1.DevWorkspaceRouting{} + dwrName := common.DevWorkspaceRoutingName(workspaceID) + Eventually(func() error { + return k8sClient.Get(ctx, namespacedName(dwrName, testNamespace), dwr) + }, timeout, interval).Should(Succeed(), "DevWorkspaceRouting should be created") + By("Manually making Routing ready to continue") markRoutingReady(testURL, common.DevWorkspaceRoutingName(workspaceID)) From 9fe3583a2c5f38572b3dc58c15c9dfe34981e027 Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Wed, 10 Dec 2025 13:15:28 +0200 Subject: [PATCH 26/30] fixup! fixup! fix Signed-off-by: Oleksii Kurinnyi --- .../workspace/devworkspace_controller.go | 99 +++++++++++++-- .../init_container_validation_test.go | 113 ++++++++++++++++-- docs/dwo-configuration.md | 4 +- 3 files changed, 191 insertions(+), 25 deletions(-) diff --git a/controllers/workspace/devworkspace_controller.go b/controllers/workspace/devworkspace_controller.go index 4b0c3bed8..868f75002 100644 --- a/controllers/workspace/devworkspace_controller.go +++ b/controllers/workspace/devworkspace_controller.go @@ -22,6 +22,7 @@ import ( "strconv" "strings" "time" + "unicode/utf8" "github.com/devfile/devworkspace-operator/pkg/library/initcontainers" "github.com/devfile/devworkspace-operator/pkg/library/ssh" @@ -69,6 +70,16 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" ) +const ( + // Validation limits for init-persistent-home container Command field + maxCommandElements = 10 + maxCommandElementLength = 1024 + + // Validation limits for init-persistent-home container Args field + maxArgsElements = 50 + maxArgsElementLength = 256 * 1024 // 256KB +) + // validateNoAdvancedFields validates that the init-persistent-home container // does not use advanced Kubernetes container fields that could make behavior unpredictable. func validateNoAdvancedFields(c corev1.Container) error { @@ -121,17 +132,17 @@ func validateHomeInitContainer(c corev1.Container) error { } } - // Only validate if present - if c.Command != nil { - if len(c.Command) != 2 || c.Command[0] != "/bin/sh" || c.Command[1] != "-c" { - return fmt.Errorf("command must be exactly [/bin/sh, -c] for %s", constants.HomeInitComponentName) + // Validate Command for security and resource limits (only if present) + if len(c.Command) > 0 { + if err := validateCommand(c.Command); err != nil { + return fmt.Errorf("invalid command for %s: %w", constants.HomeInitComponentName, err) } } - // Only validate if present - if c.Args != nil { - if len(c.Args) != 1 { - return fmt.Errorf("args must contain exactly one script string for %s", constants.HomeInitComponentName) + // Validate Args for security and resource limits (only if present) + if len(c.Args) > 0 { + if err := validateArgs(c.Args); err != nil { + return fmt.Errorf("invalid args for %s: %w", constants.HomeInitComponentName, err) } } @@ -192,13 +203,79 @@ func validateImageReference(image string) error { return nil } +// validateCommand validates the Command field for the init-persistent-home container. +// This validation prevents resource exhaustion and data corruption while allowing flexibility +// for enterprise customization. +// Generated by Claude Sonnet 4.5 +func validateCommand(command []string) error { + if len(command) > maxCommandElements { + return fmt.Errorf("command cannot exceed %d elements (got %d)", maxCommandElements, len(command)) + } + for i, cmd := range command { + if len(cmd) > maxCommandElementLength { + return fmt.Errorf("command[%d] exceeds %d characters (got %d)", i, maxCommandElementLength, len(cmd)) + } + if err := validateStringContent(cmd, fmt.Sprintf("command[%d]", i)); err != nil { + return err + } + } + return nil +} + +// validateArgs validates the Args field for the init-persistent-home container. +// This validation prevents resource exhaustion and data corruption while allowing flexibility +// for enterprise customization. +// Generated by Claude Sonnet 4.5 +func validateArgs(args []string) error { + if len(args) > maxArgsElements { + return fmt.Errorf("args cannot exceed %d elements (got %d)", maxArgsElements, len(args)) + } + for i, arg := range args { + if len(arg) > maxArgsElementLength { + return fmt.Errorf("args[%d] exceeds %d bytes (got %d bytes)", i, maxArgsElementLength, len(arg)) + } + if err := validateStringContent(arg, fmt.Sprintf("args[%d]", i)); err != nil { + return err + } + } + return nil +} + +// validateStringContent validates the content of command/args strings to prevent +// data corruption and ensure valid UTF-8 encoding. +// Generated by Claude Sonnet 4.5 +func validateStringContent(s, fieldName string) error { + // No null bytes (breaks JSON marshaling and container runtime) + if strings.Contains(s, "\x00") { + return fmt.Errorf("%s contains null bytes", fieldName) + } + + // Check for invalid control characters (allow \n, \t which are common in scripts) + for _, r := range s { + if r < 0x20 && r != '\n' && r != '\t' { + return fmt.Errorf("%s contains invalid control character (U+%04X)", fieldName, r) + } + if r == 0x7F { + return fmt.Errorf("%s contains DEL control character", fieldName) + } + } + + // Must be valid UTF-8 + if !utf8.ValidString(s) { + return fmt.Errorf("%s contains invalid UTF-8", fieldName) + } + + return nil +} + // ensureHomeInitContainerFields ensures that an init-persistent-home container has // the correct Command, Args, and VolumeMounts. func ensureHomeInitContainerFields(c *corev1.Container) error { - c.Command = []string{"/bin/sh", "-c"} - if len(c.Args) != 1 { - return fmt.Errorf("args must contain exactly one script string for %s", constants.HomeInitComponentName) + // Set default command only if not provided + if len(c.Command) == 0 { + c.Command = []string{"/bin/sh", "-c"} } + // Args are validated separately in validateCommandAndArgs c.VolumeMounts = []corev1.VolumeMount{{ Name: constants.HomeVolumeName, MountPath: constants.HomeUserDirectory, diff --git a/controllers/workspace/init_container_validation_test.go b/controllers/workspace/init_container_validation_test.go index c2c4373ad..b37201ed7 100644 --- a/controllers/workspace/init_container_validation_test.go +++ b/controllers/workspace/init_container_validation_test.go @@ -59,46 +59,42 @@ func TestValidateInitContainer(t *testing.T) { expectError: false, }, { - name: "Rejects invalid command", + name: "Accepts custom command", container: corev1.Container{ Name: constants.HomeInitComponentName, Image: "custom-image:latest", Command: []string{"/bin/sh"}, Args: []string{"echo 'test'"}, }, - expectError: true, - errorMsg: "command must be exactly [/bin/sh, -c]", + expectError: false, }, { - name: "Rejects empty command", + name: "Accepts empty command", container: corev1.Container{ Name: constants.HomeInitComponentName, Image: "custom-image:latest", Command: []string{}, Args: []string{"echo 'test'"}, }, - expectError: true, - errorMsg: "command must be exactly [/bin/sh, -c]", + expectError: false, }, { - name: "Rejects empty args", + name: "Accepts empty args", container: corev1.Container{ Name: constants.HomeInitComponentName, Image: "custom-image:latest", Args: []string{}, }, - expectError: true, - errorMsg: "args must contain exactly one script string", + expectError: false, }, { - name: "Rejects multiple args", + name: "Accepts multiple args", container: corev1.Container{ Name: constants.HomeInitComponentName, Image: "custom-image:latest", Args: []string{"echo 'test'", "echo 'test2'"}, }, - expectError: true, - errorMsg: "args must contain exactly one script string", + expectError: false, }, { name: "Rejects user-provided volumeMounts", @@ -267,3 +263,96 @@ func TestValidateImageReference(t *testing.T) { }) } } + +// makeStringSlice creates a string slice of the specified length with the given value. +// Generated by Claude Sonnet 4.5 +func makeStringSlice(count int, value string) []string { + result := make([]string, count) + for i := range count { + result[i] = value + } + return result +} + +// TestValidateCommand tests validation of Command field. +// Generated by Claude Sonnet 4.5 +func TestValidateCommand(t *testing.T) { + tests := []struct { + name string + command []string + expectError bool + errorMsg string + }{ + // Valid cases + {"Valid command", []string{"/bin/sh", "-c"}, false, ""}, + {"Empty command", []string{}, false, ""}, + {"Command with newlines and tabs", []string{"/bin/sh\ntest\ttab"}, false, ""}, + {"Max valid elements", makeStringSlice(maxCommandElements, "x"), false, ""}, + {"Max valid element length", []string{strings.Repeat("a", maxCommandElementLength)}, false, ""}, + {"Valid UTF-8 with emoji", []string{"/bin/sh", "🚀"}, false, ""}, + + // Invalid cases + {"Too many elements", makeStringSlice(maxCommandElements+1, "x"), true, "command cannot exceed"}, + {"Element too long", []string{strings.Repeat("a", maxCommandElementLength+1)}, true, "command[0] exceeds"}, + {"Null byte", []string{"/bin/sh\x00malicious"}, true, "contains null bytes"}, + {"Control character", []string{"/bin/sh\x01"}, true, "contains invalid control character"}, + {"DEL character", []string{"/bin/sh\x7F"}, true, "contains DEL control character"}, + {"Invalid UTF-8", []string{string([]byte{0xFF, 0xFE, 0xFD})}, true, "contains invalid UTF-8"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateCommand(tt.command) + + if tt.expectError { + assert.Error(t, err) + if tt.errorMsg != "" { + assert.Contains(t, err.Error(), tt.errorMsg) + } + } else { + assert.NoError(t, err) + } + }) + } +} + +// TestValidateArgs tests validation of Args field. +// Generated by Claude Sonnet 4.5 +func TestValidateArgs(t *testing.T) { + tests := []struct { + name string + args []string + expectError bool + errorMsg string + }{ + // Valid cases + {"Valid args", []string{"echo 'hello'"}, false, ""}, + {"Empty args", []string{}, false, ""}, + {"Args with newlines and tabs", []string{"#!/bin/sh\necho 'test'\n\techo 'indented'"}, false, ""}, + {"Max valid elements", make([]string, maxArgsElements), false, ""}, + {"Max valid element length", []string{strings.Repeat("a", maxArgsElementLength)}, false, ""}, + {"Valid UTF-8 with emoji", []string{"echo '🚀 Hello World 你好'"}, false, ""}, + + // Invalid cases + {"Too many elements", make([]string, maxArgsElements+1), true, "args cannot exceed"}, + {"Element too large", []string{strings.Repeat("a", maxArgsElementLength+1)}, true, "args[0] exceeds"}, + {"Null byte", []string{"echo\x00malicious"}, true, "contains null bytes"}, + {"Control character", []string{"echo\x02test"}, true, "contains invalid control character"}, + {"Invalid UTF-8", []string{string([]byte{0xFF, 0xFE})}, true, "contains invalid UTF-8"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateArgs(tt.args) + + if tt.expectError { + assert.Error(t, err) + if tt.errorMsg != "" { + assert.Contains(t, err.Error(), tt.errorMsg) + } + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/docs/dwo-configuration.md b/docs/dwo-configuration.md index 82ac55a7b..5ba9a4744 100644 --- a/docs/dwo-configuration.md +++ b/docs/dwo-configuration.md @@ -167,8 +167,8 @@ A specially-named init container `init-persistent-home` can be used to override - **Name:** Must be exactly `init-persistent-home` - **Image:** Optional. If omitted, defaults to the first non-imported workspace container's image. If no suitable image can be inferred, the workspace will fail to start with an error. -- **Command:** Optional. If omitted, defaults to `["/bin/sh", "-c"]`. If provided, must exactly match this value. -- **Args:** Required. Must contain exactly one string with the initialization script. +- **Command:** Optional. If omitted, defaults to `["/bin/sh", "-c"]`. If provided, can be any valid command array. +- **Args:** Optional. If omitted and command is also omitted, defaults to a single script string. If provided, can be any valid args array. - **VolumeMounts:** Forbidden. The operator automatically mounts the `persistent-home` volume at `/home/user/`. - **Env:** Optional. Environment variables are allowed. - **Other fields:** Not allowed. Fields such as `ports`, `probes`, `lifecycle`, `securityContext`, `resources`, `volumeDevices`, `stdin`, `tty`, and `workingDir` are rejected to keep behavior predictable. From 4c076c63e4d169e5c641bb44c0160957521c864a Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Wed, 10 Dec 2025 13:28:18 +0200 Subject: [PATCH 27/30] fixup! fixup! fixup! fix Signed-off-by: Oleksii Kurinnyi --- docs/dwo-configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dwo-configuration.md b/docs/dwo-configuration.md index 5ba9a4744..08e0e24c4 100644 --- a/docs/dwo-configuration.md +++ b/docs/dwo-configuration.md @@ -163,7 +163,7 @@ config: A specially-named init container `init-persistent-home` can be used to override the built-in persistent home directory initialization logic when `config.workspace.persistUserHome.enabled: true`. This is useful for enterprises using customized UDI images that require different home directory setup logic. -**Contract for `init-persistent-home`:** +**Prerequisites for `init-persistent-home`:** - **Name:** Must be exactly `init-persistent-home` - **Image:** Optional. If omitted, defaults to the first non-imported workspace container's image. If no suitable image can be inferred, the workspace will fail to start with an error. From 2812a14630969591b4b181957a3205019b07c61d Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Mon, 22 Dec 2025 14:58:31 +0200 Subject: [PATCH 28/30] fixup! fixup! fixup! fixup! fix Signed-off-by: Oleksii Kurinnyi --- .../workspace/devworkspace_controller.go | 213 +---------- .../init_container_validation_test.go | 358 ------------------ 2 files changed, 4 insertions(+), 567 deletions(-) delete mode 100644 controllers/workspace/init_container_validation_test.go diff --git a/controllers/workspace/devworkspace_controller.go b/controllers/workspace/devworkspace_controller.go index 868f75002..217a4117f 100644 --- a/controllers/workspace/devworkspace_controller.go +++ b/controllers/workspace/devworkspace_controller.go @@ -18,11 +18,9 @@ package controllers import ( "context" "fmt" - "regexp" "strconv" "strings" "time" - "unicode/utf8" "github.com/devfile/devworkspace-operator/pkg/library/initcontainers" "github.com/devfile/devworkspace-operator/pkg/library/ssh" @@ -70,212 +68,13 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" ) -const ( - // Validation limits for init-persistent-home container Command field - maxCommandElements = 10 - maxCommandElementLength = 1024 - - // Validation limits for init-persistent-home container Args field - maxArgsElements = 50 - maxArgsElementLength = 256 * 1024 // 256KB -) - -// validateNoAdvancedFields validates that the init-persistent-home container -// does not use advanced Kubernetes container fields that could make behavior unpredictable. -func validateNoAdvancedFields(c corev1.Container) error { - if len(c.Ports) > 0 { - return fmt.Errorf("ports are not allowed for %s", constants.HomeInitComponentName) - } - - if c.LivenessProbe != nil || c.ReadinessProbe != nil || c.StartupProbe != nil { - return fmt.Errorf("probes are not allowed for %s", constants.HomeInitComponentName) - } - - if c.Lifecycle != nil { - return fmt.Errorf("lifecycle hooks are not allowed for %s", constants.HomeInitComponentName) - } - - if c.Stdin || c.StdinOnce || c.TTY { - return fmt.Errorf("stdin/tty fields are not allowed for %s", constants.HomeInitComponentName) - } - - if len(c.VolumeDevices) > 0 { - return fmt.Errorf("volumeDevices are not allowed for %s", constants.HomeInitComponentName) - } - - if c.WorkingDir != "" { - return fmt.Errorf("workingDir is not allowed for %s", constants.HomeInitComponentName) - } - - if c.TerminationMessagePath != "" || c.TerminationMessagePolicy != "" { - return fmt.Errorf("termination message fields are not allowed for %s", constants.HomeInitComponentName) - } - - if c.SecurityContext != nil { - return fmt.Errorf("securityContext is not allowed for %s", constants.HomeInitComponentName) - } - if c.Resources.Limits != nil || c.Resources.Requests != nil { - return fmt.Errorf("resource limits/requests are not allowed for %s", constants.HomeInitComponentName) - } - - return nil -} - -// validateHomeInitContainer validates all aspects of the init-persistent-home container. -// It only validates fields that are present, as missing fields will be filled by -// the strategic merge with the default init container. -func validateHomeInitContainer(c corev1.Container) error { - // Only validate if present - if c.Image != "" { - if err := validateImageReference(c.Image); err != nil { - return fmt.Errorf("invalid image reference for %s: %w", constants.HomeInitComponentName, err) - } - } - - // Validate Command for security and resource limits (only if present) - if len(c.Command) > 0 { - if err := validateCommand(c.Command); err != nil { - return fmt.Errorf("invalid command for %s: %w", constants.HomeInitComponentName, err) - } - } - - // Validate Args for security and resource limits (only if present) - if len(c.Args) > 0 { - if err := validateArgs(c.Args); err != nil { - return fmt.Errorf("invalid args for %s: %w", constants.HomeInitComponentName, err) - } - } - - // Always validate - should not be provided - if len(c.VolumeMounts) > 0 { - return fmt.Errorf("volumeMounts are not allowed for %s; persistent-home is auto-mounted at /home/user/", constants.HomeInitComponentName) - } - - // Always validate - should not be provided - if err := validateNoAdvancedFields(c); err != nil { - return err - } - - return nil -} - -func validateImageReference(image string) error { - if image == "" { - return fmt.Errorf("image reference cannot be empty") - } - - // whitespace and control characters - if strings.ContainsAny(image, "\n\r\t ") { - return fmt.Errorf("contains invalid whitespace characters") - } - - // other control characters - for _, r := range image { - if r < 0x20 || r == 0x7F { - return fmt.Errorf("contains invalid control characters") - } - } - - // format: [registry[:port]/]repository[:tag][@digest] - imagePattern := regexp.MustCompile(`^([a-zA-Z0-9]([a-zA-Z0-9._-]*[a-zA-Z0-9])*|\[?[0-9a-fA-F:]+]?)(:\d{1,5})?(/[a-zA-Z0-9]([a-zA-Z0-9._/-]*[a-zA-Z0-9])*)*(:[a-zA-Z0-9_.-]+)?(@sha256:[a-f0-9]{64})?$`) - if !imagePattern.MatchString(image) { - return fmt.Errorf("invalid format: should match regex: %s", imagePattern.String()) - } - - // port range - portMatch := regexp.MustCompile(`:(\d{1,5})(/|:|@|$)`) - matches := portMatch.FindStringSubmatch(image) - if len(matches) > 1 { - port, err := strconv.Atoi(matches[1]) - if err != nil { - return fmt.Errorf("invalid port format: %w", err) - } - if port < 1 || port > 65535 { - return fmt.Errorf("invalid port number: %d (must be 1-65535)", port) - } - } - - // length check - if len(image) > 4096 { - return fmt.Errorf("length exceeds 4096 characters") - } - - return nil -} - -// validateCommand validates the Command field for the init-persistent-home container. -// This validation prevents resource exhaustion and data corruption while allowing flexibility -// for enterprise customization. -// Generated by Claude Sonnet 4.5 -func validateCommand(command []string) error { - if len(command) > maxCommandElements { - return fmt.Errorf("command cannot exceed %d elements (got %d)", maxCommandElements, len(command)) - } - for i, cmd := range command { - if len(cmd) > maxCommandElementLength { - return fmt.Errorf("command[%d] exceeds %d characters (got %d)", i, maxCommandElementLength, len(cmd)) - } - if err := validateStringContent(cmd, fmt.Sprintf("command[%d]", i)); err != nil { - return err - } - } - return nil -} - -// validateArgs validates the Args field for the init-persistent-home container. -// This validation prevents resource exhaustion and data corruption while allowing flexibility -// for enterprise customization. -// Generated by Claude Sonnet 4.5 -func validateArgs(args []string) error { - if len(args) > maxArgsElements { - return fmt.Errorf("args cannot exceed %d elements (got %d)", maxArgsElements, len(args)) - } - for i, arg := range args { - if len(arg) > maxArgsElementLength { - return fmt.Errorf("args[%d] exceeds %d bytes (got %d bytes)", i, maxArgsElementLength, len(arg)) - } - if err := validateStringContent(arg, fmt.Sprintf("args[%d]", i)); err != nil { - return err - } - } - return nil -} - -// validateStringContent validates the content of command/args strings to prevent -// data corruption and ensure valid UTF-8 encoding. -// Generated by Claude Sonnet 4.5 -func validateStringContent(s, fieldName string) error { - // No null bytes (breaks JSON marshaling and container runtime) - if strings.Contains(s, "\x00") { - return fmt.Errorf("%s contains null bytes", fieldName) - } - - // Check for invalid control characters (allow \n, \t which are common in scripts) - for _, r := range s { - if r < 0x20 && r != '\n' && r != '\t' { - return fmt.Errorf("%s contains invalid control character (U+%04X)", fieldName, r) - } - if r == 0x7F { - return fmt.Errorf("%s contains DEL control character", fieldName) - } - } - - // Must be valid UTF-8 - if !utf8.ValidString(s) { - return fmt.Errorf("%s contains invalid UTF-8", fieldName) - } - - return nil -} - // ensureHomeInitContainerFields ensures that an init-persistent-home container has -// the correct Command, Args, and VolumeMounts. +// the correct Command and VolumeMounts. func ensureHomeInitContainerFields(c *corev1.Container) error { // Set default command only if not provided if len(c.Command) == 0 { c.Command = []string{"/bin/sh", "-c"} } - // Args are validated separately in validateCommandAndArgs c.VolumeMounts = []corev1.VolumeMount{{ Name: constants.HomeVolumeName, MountPath: constants.HomeUserDirectory, @@ -589,7 +388,7 @@ func (r *DevWorkspaceReconciler) Reconcile(ctx context.Context, req ctrl.Request // Check if init-persistent-home should be disabled disableHomeInit := pointer.BoolDeref(workspace.Config.Workspace.PersistUserHome.DisableInitContainer, constants.DefaultDisableHomeInitContainer) - // Prepare patches: filter and preprocess init containers from config + // Filter init containers from config based on workspace settings patches := []corev1.Container{} for _, container := range workspace.Config.Workspace.InitContainers { // Special handling for init-persistent-home @@ -604,10 +403,6 @@ func (r *DevWorkspaceReconciler) Reconcile(ctx context.Context, req ctrl.Request reqLogger.Info("Skipping init-persistent-home container: DisableInitContainer is true") continue } - // Validate custom home init container - if err := validateHomeInitContainer(container); err != nil { - return r.failWorkspace(workspace, fmt.Sprintf("Invalid %s container: %s", constants.HomeInitComponentName, err), metrics.ReasonBadRequest, reqLogger, &reconcileStatus), nil - } } patches = append(patches, container) } @@ -618,11 +413,11 @@ func (r *DevWorkspaceReconciler) Reconcile(ctx context.Context, req ctrl.Request return r.failWorkspace(workspace, fmt.Sprintf("Failed to merge init containers: %s", err), metrics.ReasonBadRequest, reqLogger, &reconcileStatus), nil } - // Ensure init-persistent-home container have correct fields after merge + // Ensure init-persistent-home container has correct fields after merge for i := range merged { if merged[i].Name == constants.HomeInitComponentName { if err := ensureHomeInitContainerFields(&merged[i]); err != nil { - return r.failWorkspace(workspace, fmt.Sprintf("Invalid %s container: %s", constants.HomeInitComponentName, err), metrics.ReasonBadRequest, reqLogger, &reconcileStatus), nil + return r.failWorkspace(workspace, fmt.Sprintf("Failed to configure %s container: %s", constants.HomeInitComponentName, err), metrics.ReasonBadRequest, reqLogger, &reconcileStatus), nil } } } diff --git a/controllers/workspace/init_container_validation_test.go b/controllers/workspace/init_container_validation_test.go deleted file mode 100644 index b37201ed7..000000000 --- a/controllers/workspace/init_container_validation_test.go +++ /dev/null @@ -1,358 +0,0 @@ -// -// Copyright (c) 2019-2025 Red Hat, Inc. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -package controllers - -import ( - "strings" - "testing" - - "github.com/devfile/devworkspace-operator/pkg/constants" - "github.com/stretchr/testify/assert" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" -) - -func TestValidateInitContainer(t *testing.T) { - tests := []struct { - name string - container corev1.Container - expectError bool - errorMsg string - }{ - { - name: "Accepts container with only args (image and command will be filled by merge)", - container: corev1.Container{ - Name: constants.HomeInitComponentName, - Args: []string{"echo 'test'"}, - }, - expectError: false, - }, - { - name: "Accepts container with only image (command and args will be filled by merge)", - container: corev1.Container{ - Name: constants.HomeInitComponentName, - Image: "custom-image:latest", - }, - expectError: false, - }, - { - name: "Accepts valid command", - container: corev1.Container{ - Name: constants.HomeInitComponentName, - Image: "custom-image:latest", - Command: []string{"/bin/sh", "-c"}, - Args: []string{"echo 'test'"}, - }, - expectError: false, - }, - { - name: "Accepts custom command", - container: corev1.Container{ - Name: constants.HomeInitComponentName, - Image: "custom-image:latest", - Command: []string{"/bin/sh"}, - Args: []string{"echo 'test'"}, - }, - expectError: false, - }, - { - name: "Accepts empty command", - container: corev1.Container{ - Name: constants.HomeInitComponentName, - Image: "custom-image:latest", - Command: []string{}, - Args: []string{"echo 'test'"}, - }, - expectError: false, - }, - { - name: "Accepts empty args", - container: corev1.Container{ - Name: constants.HomeInitComponentName, - Image: "custom-image:latest", - Args: []string{}, - }, - expectError: false, - }, - { - name: "Accepts multiple args", - container: corev1.Container{ - Name: constants.HomeInitComponentName, - Image: "custom-image:latest", - Args: []string{"echo 'test'", "echo 'test2'"}, - }, - expectError: false, - }, - { - name: "Rejects user-provided volumeMounts", - container: corev1.Container{ - Name: constants.HomeInitComponentName, - Image: "custom-image:latest", - VolumeMounts: []corev1.VolumeMount{ - { - Name: "custom-volume", - MountPath: "/mnt/custom", - }, - }, - }, - expectError: true, - errorMsg: "volumeMounts are not allowed for init-persistent-home", - }, - { - name: "Allows env variables", - container: corev1.Container{ - Name: constants.HomeInitComponentName, - Image: "custom-image:latest", - Env: []corev1.EnvVar{ - { - Name: "TEST_VAR", - Value: "test-var", - }, - }, - }, - expectError: false, - }, - { - name: "Rejects ports", - container: corev1.Container{ - Name: constants.HomeInitComponentName, - Image: "custom-image:latest", - Ports: []corev1.ContainerPort{ - { - ContainerPort: 8080, - }, - }, - }, - expectError: true, - errorMsg: "ports are not allowed", - }, - { - name: "Rejects livenessProbe", - container: corev1.Container{ - Name: constants.HomeInitComponentName, - Image: "custom-image:latest", - LivenessProbe: &corev1.Probe{ - ProbeHandler: corev1.ProbeHandler{ - HTTPGet: &corev1.HTTPGetAction{ - Path: "/health", - }, - }, - }, - }, - expectError: true, - errorMsg: "probes are not allowed", - }, - { - name: "Rejects securityContext", - container: corev1.Container{ - Name: constants.HomeInitComponentName, - Image: "custom-image:latest", - SecurityContext: &corev1.SecurityContext{ - RunAsUser: new(int64), - }, - }, - expectError: true, - errorMsg: "securityContext is not allowed", - }, - { - name: "Rejects resources", - container: corev1.Container{ - Name: constants.HomeInitComponentName, - Image: "custom-image:latest", - Resources: corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceMemory: resource.MustParse("128Mi"), - }, - }, - }, - expectError: true, - errorMsg: "resource limits/requests are not allowed", - }, - { - name: "Rejects workingDir", - container: corev1.Container{ - Name: constants.HomeInitComponentName, - Image: "custom-image:latest", - WorkingDir: "/tmp", - }, - expectError: true, - errorMsg: "workingDir is not allowed", - }, - { - name: "Rejects image with whitespace", - container: corev1.Container{ - Name: constants.HomeInitComponentName, - Image: "nginx\tmalicious", - }, - expectError: true, - errorMsg: "invalid image reference", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := validateHomeInitContainer(tt.container) - - if tt.expectError { - assert.Error(t, err) - if tt.errorMsg != "" { - assert.Contains(t, err.Error(), tt.errorMsg) - } - } else { - assert.NoError(t, err) - } - }) - } -} - -func TestValidateImageReference(t *testing.T) { - tests := []struct { - name string - image string - expectError bool - errorMsg string - }{ - // Valid images - {"simple image", "nginx", false, ""}, - {"image with tag", "nginx:latest", false, ""}, - {"image with version tag", "nginx:1.21", false, ""}, - {"registry image", "docker.io/nginx", false, ""}, - {"registry with tag", "docker.io/nginx:latest", false, ""}, - {"registry with port", "localhost:5000/nginx", false, ""}, - {"registry port with tag", "localhost:5000/nginx:latest", false, ""}, - {"multi-level path", "registry.example.com/team/project/app", false, ""}, - {"with digest", "nginx@sha256:abc123def4567890abcdef1234567890abcdef1234567890abcdef1234567890", false, ""}, - {"full reference", "registry.example.com:8080/team/app:v1.2.3@sha256:abc123def4567890abcdef1234567890abcdef1234567890abcdef1234567890", false, ""}, - - // Invalid images - {"empty image", "", true, "cannot be empty"}, - {"whitespace", "nginx latest", true, "whitespace"}, - {"newline", "nginx\nlatest", true, "whitespace"}, - {"tab", "nginx\tlatest", true, "whitespace"}, - {"control char", "nginx\x00latest", true, "control characters"}, - {"invalid port 0", "registry:0/image", true, "port number"}, - {"invalid port 65536", "registry:65536/image", true, "port number"}, - {"invalid format", "-nginx", true, "invalid format: should match regex: ^([a-zA-Z0-9]([a-zA-Z0-9._-]*[a-zA-Z0-9])*|\\[?[0-9a-fA-F:]+]?)(:\\d{1,5})?(/[a-zA-Z0-9]([a-zA-Z0-9._/-]*[a-zA-Z0-9])*)*(:[a-zA-Z0-9_.-]+)?(@sha256:[a-f0-9]{64})?$"}, - {"too long", strings.Repeat("a", 4097), true, "exceeds 4096"}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := validateImageReference(tt.image) - if tt.expectError { - assert.Error(t, err) - if tt.errorMsg != "" { - assert.Contains(t, err.Error(), tt.errorMsg) - } - } else { - assert.NoError(t, err, "Image %q should be valid", tt.image) - } - }) - } -} - -// makeStringSlice creates a string slice of the specified length with the given value. -// Generated by Claude Sonnet 4.5 -func makeStringSlice(count int, value string) []string { - result := make([]string, count) - for i := range count { - result[i] = value - } - return result -} - -// TestValidateCommand tests validation of Command field. -// Generated by Claude Sonnet 4.5 -func TestValidateCommand(t *testing.T) { - tests := []struct { - name string - command []string - expectError bool - errorMsg string - }{ - // Valid cases - {"Valid command", []string{"/bin/sh", "-c"}, false, ""}, - {"Empty command", []string{}, false, ""}, - {"Command with newlines and tabs", []string{"/bin/sh\ntest\ttab"}, false, ""}, - {"Max valid elements", makeStringSlice(maxCommandElements, "x"), false, ""}, - {"Max valid element length", []string{strings.Repeat("a", maxCommandElementLength)}, false, ""}, - {"Valid UTF-8 with emoji", []string{"/bin/sh", "🚀"}, false, ""}, - - // Invalid cases - {"Too many elements", makeStringSlice(maxCommandElements+1, "x"), true, "command cannot exceed"}, - {"Element too long", []string{strings.Repeat("a", maxCommandElementLength+1)}, true, "command[0] exceeds"}, - {"Null byte", []string{"/bin/sh\x00malicious"}, true, "contains null bytes"}, - {"Control character", []string{"/bin/sh\x01"}, true, "contains invalid control character"}, - {"DEL character", []string{"/bin/sh\x7F"}, true, "contains DEL control character"}, - {"Invalid UTF-8", []string{string([]byte{0xFF, 0xFE, 0xFD})}, true, "contains invalid UTF-8"}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := validateCommand(tt.command) - - if tt.expectError { - assert.Error(t, err) - if tt.errorMsg != "" { - assert.Contains(t, err.Error(), tt.errorMsg) - } - } else { - assert.NoError(t, err) - } - }) - } -} - -// TestValidateArgs tests validation of Args field. -// Generated by Claude Sonnet 4.5 -func TestValidateArgs(t *testing.T) { - tests := []struct { - name string - args []string - expectError bool - errorMsg string - }{ - // Valid cases - {"Valid args", []string{"echo 'hello'"}, false, ""}, - {"Empty args", []string{}, false, ""}, - {"Args with newlines and tabs", []string{"#!/bin/sh\necho 'test'\n\techo 'indented'"}, false, ""}, - {"Max valid elements", make([]string, maxArgsElements), false, ""}, - {"Max valid element length", []string{strings.Repeat("a", maxArgsElementLength)}, false, ""}, - {"Valid UTF-8 with emoji", []string{"echo '🚀 Hello World 你好'"}, false, ""}, - - // Invalid cases - {"Too many elements", make([]string, maxArgsElements+1), true, "args cannot exceed"}, - {"Element too large", []string{strings.Repeat("a", maxArgsElementLength+1)}, true, "args[0] exceeds"}, - {"Null byte", []string{"echo\x00malicious"}, true, "contains null bytes"}, - {"Control character", []string{"echo\x02test"}, true, "contains invalid control character"}, - {"Invalid UTF-8", []string{string([]byte{0xFF, 0xFE})}, true, "contains invalid UTF-8"}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := validateArgs(tt.args) - - if tt.expectError { - assert.Error(t, err) - if tt.errorMsg != "" { - assert.Contains(t, err.Error(), tt.errorMsg) - } - } else { - assert.NoError(t, err) - } - }) - } -} From 51ee3630c5373b6186fb76192c172ac945709606 Mon Sep 17 00:00:00 2001 From: Oleksii Kurinnyi Date: Tue, 23 Dec 2025 15:50:25 +0200 Subject: [PATCH 29/30] fixup! fixup! fixup! fixup! fixup! fix Signed-off-by: Oleksii Kurinnyi --- controllers/workspace/devworkspace_controller.go | 15 +-------------- pkg/library/home/persistentHome.go | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/controllers/workspace/devworkspace_controller.go b/controllers/workspace/devworkspace_controller.go index 217a4117f..45a4ca8f6 100644 --- a/controllers/workspace/devworkspace_controller.go +++ b/controllers/workspace/devworkspace_controller.go @@ -68,19 +68,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" ) -// ensureHomeInitContainerFields ensures that an init-persistent-home container has -// the correct Command and VolumeMounts. -func ensureHomeInitContainerFields(c *corev1.Container) error { - // Set default command only if not provided - if len(c.Command) == 0 { - c.Command = []string{"/bin/sh", "-c"} - } - c.VolumeMounts = []corev1.VolumeMount{{ - Name: constants.HomeVolumeName, - MountPath: constants.HomeUserDirectory, - }} - return nil -} const ( startingWorkspaceRequeueInterval = 5 * time.Second @@ -416,7 +403,7 @@ func (r *DevWorkspaceReconciler) Reconcile(ctx context.Context, req ctrl.Request // Ensure init-persistent-home container has correct fields after merge for i := range merged { if merged[i].Name == constants.HomeInitComponentName { - if err := ensureHomeInitContainerFields(&merged[i]); err != nil { + if err := home.EnsureHomeInitContainerFields(&merged[i]); err != nil { return r.failWorkspace(workspace, fmt.Sprintf("Failed to configure %s container: %s", constants.HomeInitComponentName, err), metrics.ReasonBadRequest, reqLogger, &reconcileStatus), nil } } diff --git a/pkg/library/home/persistentHome.go b/pkg/library/home/persistentHome.go index 3ab75ca52..1a8ec0c07 100644 --- a/pkg/library/home/persistentHome.go +++ b/pkg/library/home/persistentHome.go @@ -22,6 +22,7 @@ import ( devfilevalidation "github.com/devfile/api/v2/pkg/validation" "github.com/devfile/devworkspace-operator/pkg/provision/storage" "k8s.io/utils/pointer" + corev1 "k8s.io/api/core/v1" "github.com/devfile/devworkspace-operator/pkg/common" "github.com/devfile/devworkspace-operator/pkg/constants" @@ -227,3 +228,17 @@ func inferInitContainer(dwTemplateSpec *v1alpha2.DevWorkspaceTemplateSpec) *v1al } return nil } + +// EnsureHomeInitContainerFields ensures that an init-persistent-home container has +// the correct Command and VolumeMounts. +func EnsureHomeInitContainerFields(c *corev1.Container) error { + // Set default command only if not provided + if len(c.Command) == 0 { + c.Command = []string{"/bin/sh", "-c"} + } + c.VolumeMounts = []corev1.VolumeMount{{ + Name: constants.HomeVolumeName, + MountPath: constants.HomeUserDirectory, + }} + return nil +} From 0609547b0e0789476ca9420b6834dedaaead7302 Mon Sep 17 00:00:00 2001 From: David Kwon Date: Tue, 23 Dec 2025 16:57:24 -0500 Subject: [PATCH 30/30] Run make fmt Signed-off-by: David Kwon --- controllers/workspace/devworkspace_controller.go | 1 - pkg/library/home/persistentHome.go | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/controllers/workspace/devworkspace_controller.go b/controllers/workspace/devworkspace_controller.go index 45a4ca8f6..e646b77b6 100644 --- a/controllers/workspace/devworkspace_controller.go +++ b/controllers/workspace/devworkspace_controller.go @@ -68,7 +68,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" ) - const ( startingWorkspaceRequeueInterval = 5 * time.Second ) diff --git a/pkg/library/home/persistentHome.go b/pkg/library/home/persistentHome.go index 1a8ec0c07..534fea9dc 100644 --- a/pkg/library/home/persistentHome.go +++ b/pkg/library/home/persistentHome.go @@ -21,8 +21,8 @@ import ( "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" devfilevalidation "github.com/devfile/api/v2/pkg/validation" "github.com/devfile/devworkspace-operator/pkg/provision/storage" - "k8s.io/utils/pointer" corev1 "k8s.io/api/core/v1" + "k8s.io/utils/pointer" "github.com/devfile/devworkspace-operator/pkg/common" "github.com/devfile/devworkspace-operator/pkg/constants"