1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Fix envFrom.prefix not being applied in container's env view (#5434)

This commit is contained in:
Sebastian Malton 2022-09-02 10:49:53 -07:00 committed by GitHub
parent 6ef284560a
commit bab1b13317
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 891 additions and 248 deletions

View File

@ -4,7 +4,7 @@
*/
import assert from "assert";
import type { PodContainer, PodContainerStatus } from "../endpoints";
import type { Container, PodContainerStatus } from "../endpoints";
import { Pod } from "../endpoints";
interface GetDummyPodOptions {
@ -22,8 +22,8 @@ function getDummyPod(rawOpts: GetDummyPodOptions = {}): Pod {
initRunning = 0,
} = rawOpts;
const containers: PodContainer[] = [];
const initContainers: PodContainer[] = [];
const containers: Container[] = [];
const initContainers: Container[] = [];
const containerStatuses: PodContainerStatus[] = [];
const initContainerStatuses: PodContainerStatus[] = [];
const pod = new Pod({
@ -58,7 +58,7 @@ function getDummyPod(rawOpts: GetDummyPodOptions = {}): Pod {
containers.push({
image: "dummy",
imagePullPolicy: "dummy",
imagePullPolicy: "Always",
name,
});
containerStatuses.push({
@ -80,7 +80,7 @@ function getDummyPod(rawOpts: GetDummyPodOptions = {}): Pod {
containers.push({
image: "dummy",
imagePullPolicy: "dummy",
imagePullPolicy: "Always",
name,
});
containerStatuses.push({
@ -105,7 +105,7 @@ function getDummyPod(rawOpts: GetDummyPodOptions = {}): Pod {
initContainers.push({
image: "dummy",
imagePullPolicy: "dummy",
imagePullPolicy: "Always",
name,
});
initContainerStatuses.push({
@ -127,7 +127,7 @@ function getDummyPod(rawOpts: GetDummyPodOptions = {}): Pod {
initContainers.push({
image: "dummy",
imagePullPolicy: "dummy",
imagePullPolicy: "Always",
name,
});
initContainerStatuses.push({
@ -169,7 +169,7 @@ describe("Pods", () => {
function getNamedContainer(name: string) {
return {
image: "dummy",
imagePullPolicy: "dummy",
imagePullPolicy: "Always",
name,
};
}

View File

@ -41,3 +41,4 @@ export * from "./service-account.api";
export * from "./stateful-set.api";
export * from "./storage-class.api";
export * from "./legacy-globals";
export * from "./types";

View File

@ -6,7 +6,8 @@
import type { DerivedKubeApiOptions, IgnoredKubeApiOptions } from "../kube-api";
import { KubeApi } from "../kube-api";
import { metricsApi } from "./metrics.api";
import type { PodContainer, PodMetricData, PodSpec } from "./pod.api";
import type { PodMetricData, PodSpec } from "./pod.api";
import type { Container } from "./types/container";
import type { KubeObjectStatus, LabelSelector, NamespaceScopedMetadata } from "../kube-object";
import { KubeObject } from "../kube-object";
@ -23,7 +24,7 @@ export interface JobSpec {
};
spec: PodSpec;
};
containers?: PodContainer[];
containers?: Container[];
restartPolicy?: string;
terminationGracePeriodSeconds?: number;
dnsPolicy?: string;

View File

@ -8,11 +8,15 @@ import { metricsApi } from "./metrics.api";
import type { DerivedKubeApiOptions, IgnoredKubeApiOptions, ResourceDescriptor } from "../kube-api";
import { KubeApi } from "../kube-api";
import type { RequireExactlyOne } from "type-fest";
import type { KubeObjectMetadata, LocalObjectReference, Affinity, Toleration, LabelSelector, NamespaceScopedMetadata } from "../kube-object";
import type { KubeObjectMetadata, LocalObjectReference, Affinity, Toleration, NamespaceScopedMetadata } from "../kube-object";
import type { SecretReference } from "./secret.api";
import type { PersistentVolumeClaimSpec } from "./persistent-volume-claim.api";
import { KubeObject } from "../kube-object";
import { isDefined } from "../../utils";
import type { PodSecurityContext } from "./types/pod-security-context";
import type { Probe } from "./types/probe";
import type { Container } from "./types/container";
import type { ObjectFieldSelector, ResourceFieldSelector } from "./types";
export class PodApi extends KubeApi<Pod> {
constructor(opts: DerivedKubeApiOptions & IgnoredKubeApiOptions = {}) {
@ -83,93 +87,6 @@ export enum PodStatusPhase {
EVICTED = "Evicted",
}
export interface ContainerPort {
containerPort: number;
hostIP?: string;
hostPort?: number;
name?: string;
protocol?: "UDP" | "TCP" | "SCTP";
}
export interface VolumeMount {
name: string;
readOnly?: boolean;
mountPath: string;
mountPropagation?: string;
subPath?: string;
subPathExpr?: string;
}
export interface PodContainer extends Partial<Record<PodContainerProbe, IContainerProbe>> {
name: string;
image: string;
command?: string[];
args?: string[];
ports?: ContainerPort[];
resources?: {
limits?: {
cpu: string;
memory: string;
};
requests?: {
cpu: string;
memory: string;
};
};
terminationMessagePath?: string;
terminationMessagePolicy?: string;
env?: {
name: string;
value?: string;
valueFrom?: {
fieldRef?: {
apiVersion: string;
fieldPath: string;
};
secretKeyRef?: {
key: string;
name: string;
};
configMapKeyRef?: {
key: string;
name: string;
};
};
}[];
envFrom?: {
configMapRef?: LocalObjectReference;
secretRef?: LocalObjectReference;
}[];
volumeMounts?: VolumeMount[];
imagePullPolicy?: string;
}
export type PodContainerProbe = "livenessProbe" | "readinessProbe" | "startupProbe";
interface IContainerProbe {
httpGet?: {
path?: string;
/**
* either a port number or an IANA_SVC_NAME string referring to a port defined in the container
*/
port: number | string;
scheme: string;
host?: string;
};
exec?: {
command: string[];
};
tcpSocket?: {
port: number;
};
initialDelaySeconds?: number;
timeoutSeconds?: number;
periodSeconds?: number;
successThreshold?: number;
failureThreshold?: number;
}
export interface ContainerStateRunning {
startedAt: string;
}
@ -459,17 +376,6 @@ export interface ConfigMapProjection {
optional?: boolean;
}
export interface ObjectFieldSelector {
fieldPath: string;
apiVersion?: string;
}
export interface ResourceFieldSelector {
resource: string;
containerName?: string;
divisor?: string;
}
export interface DownwardAPIVolumeFile {
path: string;
fieldRef?: ObjectFieldSelector;
@ -674,43 +580,11 @@ export interface HostAlias {
hostnames: string[];
}
export interface SELinuxOptions {
level?: string;
role?: string;
type?: string;
user?: string;
}
export interface SeccompProfile {
localhostProfile?: string;
type: string;
}
export interface Sysctl {
name: string;
value: string;
}
export interface WindowsSecurityContextOptions {
labelSelector?: LabelSelector;
maxSkew: number;
topologyKey: string;
whenUnsatisfiable: string;
}
export interface PodSecurityContext {
fsGroup?: number;
fsGroupChangePolicy?: string;
runAsGroup?: number;
runAsNonRoot?: boolean;
runAsUser?: number;
seLinuxOptions?: SELinuxOptions;
seccompProfile?: SeccompProfile;
supplementalGroups?: number[];
sysctls?: Sysctl;
windowsOptions?: WindowsSecurityContextOptions;
}
export interface TopologySpreadConstraint {
}
@ -719,7 +593,7 @@ export interface PodSpec {
activeDeadlineSeconds?: number;
affinity?: Affinity;
automountServiceAccountToken?: boolean;
containers?: PodContainer[];
containers?: Container[];
dnsPolicy?: string;
enableServiceLinks?: boolean;
ephemeralContainers?: unknown[];
@ -729,7 +603,7 @@ export interface PodSpec {
hostNetwork?: boolean;
hostPID?: boolean;
imagePullSecrets?: LocalObjectReference[];
initContainers?: PodContainer[];
initContainers?: Container[];
nodeName?: string;
nodeSelector?: Partial<Record<string, string>>;
overhead?: Partial<Record<string, string>>;
@ -931,44 +805,45 @@ export class Pod extends KubeObject<
return this.getStatusPhase() !== "Running";
}
getLivenessProbe(container: PodContainer) {
return this.getProbe(container, "livenessProbe");
getLivenessProbe(container: Container) {
return this.getProbe(container, container.livenessProbe);
}
getReadinessProbe(container: PodContainer) {
return this.getProbe(container, "readinessProbe");
getReadinessProbe(container: Container) {
return this.getProbe(container, container.readinessProbe);
}
getStartupProbe(container: PodContainer) {
return this.getProbe(container, "startupProbe");
getStartupProbe(container: Container) {
return this.getProbe(container, container.startupProbe);
}
private getProbe(container: PodContainer, field: PodContainerProbe): string[] {
const probe: string[] = [];
const probeData = container[field];
private getProbe(container: Container, probe: Probe | undefined): string[] {
const probeItems: string[] = [];
if (!probeData) {
return probe;
if (!probe) {
return probeItems;
}
const {
httpGet, exec, tcpSocket,
httpGet,
exec,
tcpSocket,
initialDelaySeconds = 0,
timeoutSeconds = 0,
periodSeconds = 0,
successThreshold = 0,
failureThreshold = 0,
} = probeData;
} = probe;
// HTTP Request
if (httpGet) {
const { path = "", port, host = "", scheme } = httpGet;
const { path = "", port, host = "", scheme = "HTTP" } = httpGet;
const resolvedPort = typeof port === "number"
? port
// Try and find the port number associated witht the name or fallback to the name itself
: container.ports?.find(containerPort => containerPort.name === port)?.containerPort || port;
probe.push(
probeItems.push(
"http-get",
`${scheme.toLowerCase()}://${host}:${resolvedPort}${path}`,
);
@ -976,15 +851,15 @@ export class Pod extends KubeObject<
// Command
if (exec?.command) {
probe.push(`exec [${exec.command.join(" ")}]`);
probeItems.push(`exec [${exec.command.join(" ")}]`);
}
// TCP Probe
if (tcpSocket?.port) {
probe.push(`tcp-socket :${tcpSocket.port}`);
probeItems.push(`tcp-socket :${tcpSocket.port}`);
}
probe.push(
probeItems.push(
`delay=${initialDelaySeconds}s`,
`timeout=${timeoutSeconds}s`,
`period=${periodSeconds}s`,
@ -992,7 +867,7 @@ export class Pod extends KubeObject<
`#failure=${failureThreshold}`,
);
return probe;
return probeItems;
}
getNodeName(): string | undefined {

View File

@ -0,0 +1,19 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
/**
* Adds and removes POSIX capabilities from running containers.
*/
export interface Capabilities {
/**
* Added capabilities
*/
add?: string[];
/**
* Removed capabilities
*/
drop?: string[];
}

View File

@ -0,0 +1,12 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
export interface ContainerPort {
containerPort: number;
hostIP?: string;
hostPort?: number;
name?: string;
protocol?: "UDP" | "TCP" | "SCTP";
}

View File

@ -0,0 +1,176 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { Lifecycle } from "./lifecycle";
import type { ResourceRequirements } from "./resource-requirements";
import type { SecurityContext } from "./security-context";
import type { Probe } from "./probe";
import type { VolumeDevice } from "./volume-device";
import type { VolumeMount } from "./volume-mount";
import type { ContainerPort } from "./container-port";
import type { EnvFromSource } from "./env-from-source";
import type { EnvVar } from "./env-var";
/**
* A single application container that you want to run within a pod.
*/
export interface Container {
/**
* Arguments to the entrypoint. The docker 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
*/
args?: string[];
/**
* Entrypoint array. Not executed within a shell. The docker 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
*/
command?: string[];
/**
* List of environment variables to set in the container. Cannot be updated.
*/
env?: EnvVar[];
/**
* List of sources to populate environment variables in the container. The keys defined within a
* source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the
* container is starting.
*
* 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.
*/
envFrom?: EnvFromSource[];
/**
* Docker image name.
*
* More info: https://kubernetes.io/docs/concepts/containers/images
*/
image?: string;
/**
* Image pull policy. 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
*/
imagePullPolicy?: "Always" | "Never" | "IfNotPresent";
lifecycle?: Lifecycle;
livenessProbe?: Probe;
/**
* Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique
* name. Cannot be updated.
*/
name: string;
/**
* List of ports to expose from the container. Exposing a port here gives the system additional
* information about the network connections a container uses, but is primarily informational.
* 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. Cannot be updated.
*/
ports?: ContainerPort[];
readinessProbe?: Probe;
resources?: ResourceRequirements;
securityContext?: SecurityContext;
startupProbe?: Probe;
/**
* 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 false
*/
stdin?: boolean;
/**
* 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 false
*/
stdinOnce?: boolean;
/**
* 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. Cannot be updated.
*
* @default "/dev/termination-log"
*/
terminationMessagePath?: string;
/**
* Indicate how the termination message should be populated.
*
* - `File`: will use the contents of {@link 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. Cannot be updated.
*
* @default "File"
*/
terminationMessagePolicy?: "File" | "FallbackToLogsOnError";
/**
* Whether this container should allocate a TTY for itself, also requires 'stdin' to be true.
*
* @default false
*/
tty?: boolean;
/**
* volumeDevices is the list of block devices to be used by the container.
*/
volumeDevices?: VolumeDevice[];
/**
* Pod volumes to mount into the container's filesystem. Cannot be updated.
*/
volumeMounts?: VolumeMount[];
/**
* 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.
*/
workingDir?: string;
}

View File

@ -0,0 +1,14 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { EnvSource } from "./env-source";
export interface EnvFromSource {
configMapRef?: EnvSource;
/**
* An identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.
*/
prefix?: string;
secretRef?: EnvSource;
}

View File

@ -0,0 +1,13 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { LocalObjectReference } from "../../kube-object";
export interface EnvSource extends LocalObjectReference {
/**
* Whether the object must be defined
*/
optional?: boolean;
}

View File

@ -0,0 +1,10 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
export interface EnvVarKeySelector {
key: string;
name?: string;
optional?: boolean;
}

View File

@ -0,0 +1,15 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { EnvVarKeySelector } from "./env-var-key-selector";
import type { ObjectFieldSelector } from "./object-field-selector";
import type { ResourceFieldSelector } from "./resource-field-selector";
export interface EnvVarSource {
configMapKeyRef?: EnvVarKeySelector;
fieldRef?: ObjectFieldSelector;
resourceFieldRef?: ResourceFieldSelector;
secretKeyRef?: EnvVarKeySelector;
}

View File

@ -0,0 +1,12 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { EnvVarSource } from "./env-var-source";
export interface EnvVar {
name: string;
value?: string;
valueFrom?: EnvVarSource;
}

View File

@ -0,0 +1,19 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
/**
* ExecAction describes a "run in container" action.
*/
export interface ExecAction {
/**
* 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.
*/
command?: string[];
}

View File

@ -0,0 +1,17 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { ExecAction } from "./exec-action";
import type { HttpGetAction } from "./http-get-action";
import type { TcpSocketAction } from "./tcp-socket-action";
/**
* Handler defines a specific action that should be taken.
*/
export interface Handler {
exec?: ExecAction;
httpGet?: HttpGetAction;
tcpSocket?: TcpSocketAction;
}

View File

@ -0,0 +1,38 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { HttpHeader } from "./http-header";
/**
* An action based on HTTP Get requests.
*/
export interface HttpGetAction {
/**
* Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.
*/
host?: string;
/**
* Custom headers to set in the request. HTTP allows repeated headers.
*/
httpHeaders?: HttpHeader[];
/**
* Path to access on the HTTP server.
*/
path?: string;
/**
* The PORT to request from.
*/
port: string | number;
/**
* Scheme to use for connecting to the host.
*
* @default "HTTP"
*/
scheme?: string;
}

View File

@ -0,0 +1,19 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
/**
* A custom header to be used in HTTP probes and get actions
*/
export interface HttpHeader {
/**
* Field name
*/
name: string;
/**
* The value of the field
*/
value: string;
}

View File

@ -0,0 +1,36 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
export * from "./aggregation-rule";
export * from "./capabilities";
export * from "./container";
export * from "./container-port";
export * from "./env-from-source";
export * from "./env-source";
export * from "./env-var-key-selector";
export * from "./env-var-source";
export * from "./env-var";
export * from "./exec-action";
export * from "./handler";
export * from "./http-get-action";
export * from "./http-header";
export * from "./job-template-spec";
export * from "./lifecycle";
export * from "./object-field-selector";
export * from "./persistent-volume-claim-template-spec";
export * from "./pod-security-context";
export * from "./pod-template-spec";
export * from "./policy-rule";
export * from "./probe";
export * from "./resource-field-selector";
export * from "./resource-requirements";
export * from "./role-ref";
export * from "./se-linux-options";
export * from "./seccomp-profile";
export * from "./subject";
export * from "./tcp-socket-action";
export * from "./volume-device";
export * from "./volume-mount";
export * from "./windows-security-context-options";

View File

@ -0,0 +1,17 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { Handler } from "./handler";
/**
* Lifecycle describes actions that the management system should take in response to container
* lifecycle events. For the PostStart and PreStop lifecycle handlers, management of the container
* blocks until the action is complete, unless the container process fails, in which case the
* handler is aborted.
*/
export interface Lifecycle {
postStart?: Handler;
preStop?: Handler;
}

View File

@ -0,0 +1,9 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
export interface ObjectFieldSelector {
apiVersion?: string;
fieldPath: string;
}

View File

@ -0,0 +1,22 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { SeLinuxOptions } from "./se-linux-options";
import type { SeccompProfile } from "./seccomp-profile";
import type { WindowsSecurityContextOptions } from "./windows-security-context-options";
import type { Sysctl } from "../pod.api";
export interface PodSecurityContext {
fsGroup?: number;
fsGroupChangePolicy?: string;
runAsGroup?: number;
runAsNonRoot?: boolean;
runAsUser?: number;
seLinuxOptions?: SeLinuxOptions;
seccompProfile?: SeccompProfile;
supplementalGroups?: number[];
sysctls?: Sysctl;
windowsOptions?: WindowsSecurityContextOptions;
}

View File

@ -0,0 +1,80 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { ExecAction } from "./exec-action";
import type { HttpGetAction } from "./http-get-action";
import type { TcpSocketAction } from "./tcp-socket-action";
/**
* Describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.
*/
export interface Probe {
exec?: ExecAction;
/**
* Minimum consecutive failures for the probe to be considered failed after having succeeded.
*
* @default 3
* @minimum 1
*/
failureThreshold?: number;
httpGet?: HttpGetAction;
/**
* Duration after the container has started before liveness probes are initiated.
*
* More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
*/
initialDelaySeconds?: number;
/**
* How often to perform the probe.
*
* @default 10
* @minimum 1
*/
periodSeconds?: number;
/**
* Minimum consecutive successes for the probe to be considered successful after having failed.
*
* Must be 1 for liveness and startup.
*
* @default 1
* @minimum 1
*/
successThreshold?: number;
tcpSocket?: TcpSocketAction;
/**
* Duration 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 not set, 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 1
*/
terminationGracePeriodSeconds?: number;
/**
* Duration after which the probe times out.
*
* More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
*
* @default 1
* @minimum 1
*/
timeoutSeconds?: number;
}

View File

@ -0,0 +1,10 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
export interface ResourceFieldSelector {
containerName?: string;
divisor?: string;
resource: string;
}

View File

@ -0,0 +1,29 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
/**
* SELinuxOptions are the labels to be applied to the container
*/
export interface SeLinuxOptions {
/**
* The SELinux `level` label that applies to the container.
*/
level?: string;
/**
* The SELinux `role` label that applies to the container.
*/
role?: string;
/**
* The SELinux `type` label that applies to the container.
*/
type?: string;
/**
* The SELinux `user` label that applies to the container.
*/
user?: string;
}

View File

@ -0,0 +1,29 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
/**
* Defines a pod's or a container's seccomp profile settings. Only one profile source may be set.
*/
export interface SeccompProfile {
/**
* 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 only be set if type is "Localhost".
*/
localhostProfile?: string;
/**
* Indicates which kind of seccomp profile will be applied.
*
* Options:
*
* | Value | Description |
* |--|--|
* | `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: "Localhost" | "RuntimeDefault" | "Unconfined";
}

View File

@ -0,0 +1,55 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { Capabilities } from "./capabilities";
import type { SeLinuxOptions } from "./se-linux-options";
import type { SeccompProfile } from "./seccomp-profile";
import type { WindowsSecurityContextOptions } from "./windows-security-context-options";
/**
* SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence.
*/
export interface SecurityContext {
/**
* 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
*/
allowPrivilegeEscalation?: boolean;
capabilities?: Capabilities;
/**
* Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false.
*/
privileged?: boolean;
/**
* procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled.
*/
procMount?: string;
/**
* Whether this container has a read-only root filesystem. Default is false.
*/
readOnlyRootFilesystem?: boolean;
/**
* 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.
*/
runAsGroup?: number;
/**
* 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.
*/
runAsNonRoot?: boolean;
/**
* 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.
*/
runAsUser?: number;
seLinuxOptions?: SeLinuxOptions;
seccompProfile?: SeccompProfile;
windowsOptions?: WindowsSecurityContextOptions;
}

View File

@ -0,0 +1,19 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
/**
* An action based on opening a socket
*/
export interface TcpSocketAction {
/**
* Host name to connect to, defaults to the pod IP.
*/
host?: string;
/**
* Port to connect to
*/
port: number | string;
}

View File

@ -0,0 +1,19 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
/**
* A mapping of a raw block device within a container.
*/
export interface VolumeDevice {
/**
* The path inside of the container that the device will be mapped to.
*/
devicePath: string;
/**
* Must match the name of a persistentVolumeClaim in the pod
*/
name: string;
}

View File

@ -0,0 +1,13 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
export interface VolumeMount {
name: string;
readOnly?: boolean;
mountPath: string;
mountPropagation?: string;
subPath?: string;
subPathExpr?: string;
}

View File

@ -0,0 +1,40 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
/**
* Windows-specific options and credentials.
*/
export interface WindowsSecurityContextOptions {
/**
* The location of the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa)
* inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field.
*/
gmsaCredentialSpec?: string;
/**
* The name of the GMSA credential spec to use.
*/
gmsaCredentialSpecName?: string;
/**
* Determines if a container should be run as a 'Host Process' container.
*
* This field is alpha-level and will only be honored by components that enable the
* WindowsHostProcessContainers feature flag.
*
* Setting this field without the feature flag will result in errors when validating the Pod.
*
* 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.
*/
hostProcess?: boolean;
/**
* 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.
*/
runAsUserName?: string;
}

View File

@ -47,7 +47,7 @@ export {
} from "../../common/k8s-api/kube-object.store";
export {
type PodContainer as IPodContainer,
type Container as IPodContainer,
type PodContainerStatus as IPodContainerStatus,
Pod,
PodApi as PodsApi,

View File

@ -47,8 +47,8 @@ const dummyDeployment = new Deployment({
},
},
terminationMessagePath: "dummy",
terminationMessagePolicy: "dummy",
imagePullPolicy: "dummy",
terminationMessagePolicy: "File",
imagePullPolicy: "Always",
}],
restartPolicy: "dummy",
terminationGracePeriodSeconds: 10,

View File

@ -7,26 +7,39 @@ import "./pod-container-env.scss";
import React, { useEffect, useState } from "react";
import { observer } from "mobx-react";
import type { PodContainer, Secret } from "../../../common/k8s-api/endpoints";
import type { Container, EnvVarKeySelector, Secret } from "../../../common/k8s-api/endpoints";
import { DrawerItem } from "../drawer";
import { autorun } from "mobx";
import { secretStore } from "../+config-secrets/legacy-store";
import { configMapStore } from "../+config-maps/legacy-store";
import { Icon } from "../icon";
import { base64, cssNames, iter } from "../../utils";
import { base64, cssNames, object } from "../../utils";
import _ from "lodash";
import { withInjectables } from "@ogre-tools/injectable-react";
import type { ConfigMapStore } from "../+config-maps/store";
import type { SecretStore } from "../+config-secrets/store";
import configMapStoreInjectable from "../+config-maps/store.injectable";
import secretStoreInjectable from "../+config-secrets/store.injectable";
export interface ContainerEnvironmentProps {
container: PodContainer;
container: Container;
namespace: string;
}
export const ContainerEnvironment = observer((props: ContainerEnvironmentProps) => {
const { container: { env, envFrom = [] }, namespace } = props;
interface Dependencies {
configMapStore: ConfigMapStore;
secretStore: SecretStore;
}
const NonInjectedContainerEnvironment = observer((props: Dependencies & ContainerEnvironmentProps) => {
const {
container: { env, envFrom = [] },
namespace,
configMapStore,
secretStore,
} = props;
useEffect( () => autorun(() => {
for (const { valueFrom } of env ?? []) {
if (valueFrom?.configMapKeyRef) {
if (valueFrom?.configMapKeyRef?.name) {
configMapStore.load({ name: valueFrom.configMapKeyRef.name, namespace });
}
}
@ -51,27 +64,22 @@ export const ContainerEnvironment = observer((props: ContainerEnvironmentProps)
if (value) {
secretValue = value;
}
if (valueFrom) {
} else if (valueFrom) {
const { fieldRef, secretKeyRef, configMapKeyRef } = valueFrom;
if (fieldRef) {
const { apiVersion, fieldPath } = fieldRef;
secretValue = `fieldRef(${apiVersion}:${fieldPath})`;
}
if (secretKeyRef) {
} else if (secretKeyRef?.name) {
secretValue = (
<SecretKey
reference={secretKeyRef}
namespace={namespace}
secretStore={secretStore}
/>
);
}
if (configMapKeyRef) {
} else if (configMapKeyRef?.name) {
const { name, key } = configMapKeyRef;
const configMap = configMapStore.getByName(name, namespace);
@ -91,60 +99,61 @@ export const ContainerEnvironment = observer((props: ContainerEnvironmentProps)
});
};
const renderEnvFrom = () => {
return Array.from(iter.filterFlatMap(envFrom, vars => {
if (vars.configMapRef?.name) {
return renderEnvFromConfigMap(vars.configMapRef.name);
}
const renderEnvFrom = () => (
envFrom
.flatMap(({ configMapRef, secretRef, prefix }) => {
if (configMapRef?.name) {
return renderEnvFromConfigMap(configMapRef.name, prefix);
}
if (vars.secretRef?.name) {
return renderEnvFromSecret(vars.secretRef.name);
}
if (secretRef?.name) {
return renderEnvFromSecret(secretRef.name, prefix);
}
return null;
}));
};
return null;
})
);
const renderEnvFromConfigMap = (configMapName: string) => {
const renderEnvFromConfigMap = (configMapName: string, prefix: string | undefined) => {
const configMap = configMapStore.getByName(configMapName, namespace);
if (!configMap) return null;
return Object.entries(configMap.data).map(([name, value]) => (
<div className="variable" key={name}>
<span className="var-name">{name}</span>
:
{value}
</div>
));
return object.entries(configMap.data)
.map(([name, value]) => (
<div className="variable" key={name}>
<span className="var-name">
{prefix}
{name}
</span>
:
{value}
</div>
));
};
const renderEnvFromSecret = (secretName: string) => {
const renderEnvFromSecret = (secretName: string, prefix: string | undefined) => {
const secret = secretStore.getByName(secretName, namespace);
if (!secret) return null;
return Object.keys(secret.data).map(key => {
const secretKeyRef = {
name: secret.getName(),
key,
};
const value = (
<SecretKey
reference={secretKeyRef}
namespace={namespace}
/>
);
return (
return Object.keys(secret.data)
.map(key => (
<div className="variable" key={key}>
<span className="var-name">{key}</span>
<span className="var-name">
{prefix}
{key}
</span>
:
{value}
<SecretKey
reference={{
name: secret.getName(),
key,
}}
namespace={namespace}
secretStore={secretStore} />
</div>
);
});
));
};
return (
@ -155,19 +164,34 @@ export const ContainerEnvironment = observer((props: ContainerEnvironmentProps)
);
});
export interface SecretKeyProps {
reference: {
name: string;
key: string;
};
export const ContainerEnvironment = withInjectables<Dependencies, ContainerEnvironmentProps>(NonInjectedContainerEnvironment, {
getProps: (di, props) => ({
...props,
configMapStore: di.inject(configMapStoreInjectable),
secretStore: di.inject(secretStoreInjectable),
}),
});
interface SecretKeyProps {
reference: EnvVarKeySelector;
namespace: string;
secretStore: SecretStore;
}
const SecretKey = (props: SecretKeyProps) => {
const { reference: { name, key }, namespace } = props;
const {
reference: { name, key },
namespace,
secretStore,
} = props;
const [loading, setLoading] = useState(false);
const [secret, setSecret] = useState<Secret>();
if (!name) {
return null;
}
const showKey = async (evt: React.MouseEvent) => {
evt.preventDefault();
setLoading(true);

View File

@ -6,7 +6,7 @@
import "./pod-details-container.scss";
import React from "react";
import type { PodContainer, PodContainerStatus, Pod } from "../../../common/k8s-api/endpoints";
import type { Container, PodContainerStatus, Pod } from "../../../common/k8s-api/endpoints";
import { DrawerItem } from "../drawer";
import { cssNames, isDefined } from "../../utils";
import { StatusBrick } from "../status-brick";
@ -27,7 +27,7 @@ import getActiveClusterEntityInjectable from "../../api/catalog/entity/get-activ
export interface PodDetailsContainerProps {
pod: Pod;
container: PodContainer;
container: Container;
metrics?: Partial<Record<string, MetricData>>;
}

View File

@ -43,12 +43,12 @@ const dummyReplicaSet = new ReplicaSet({
containers: [{
name: "dummy",
image: "dummy",
imagePullPolicy: "dummy",
imagePullPolicy: "Always",
}],
initContainers: [{
name: "dummy",
image: "dummy",
imagePullPolicy: "dummy",
imagePullPolicy: "Always",
}],
priority: 1,
serviceAccountName: "dummy",

View File

@ -23,8 +23,8 @@ const spec: PodSpec = {
},
},
terminationMessagePath: "test",
terminationMessagePolicy: "test",
imagePullPolicy: "test",
terminationMessagePolicy: "File",
imagePullPolicy: "Always",
}],
restartPolicy: "restart",
terminationGracePeriodSeconds: 1200,

View File

@ -22,7 +22,7 @@ export const dockerPod = new Pod({
{
name: "docker-exporter",
image: "docker.io/prom/node-exporter:v1.0.0-rc.0",
imagePullPolicy: "pull",
imagePullPolicy: "IfNotPresent",
},
],
serviceAccountName: "dummy",
@ -66,24 +66,24 @@ export const deploymentPod1 = new Pod({
{
name: "init-node-exporter",
image: "docker.io/prom/node-exporter:v1.0.0-rc.0",
imagePullPolicy: "pull",
imagePullPolicy: "IfNotPresent",
},
{
name: "init-node-exporter-1",
image: "docker.io/prom/node-exporter:v1.0.0-rc.0",
imagePullPolicy: "pull",
imagePullPolicy: "IfNotPresent",
},
],
containers: [
{
name: "node-exporter",
image: "docker.io/prom/node-exporter:v1.0.0-rc.0",
imagePullPolicy: "pull",
imagePullPolicy: "IfNotPresent",
},
{
name: "node-exporter-1",
image: "docker.io/prom/node-exporter:v1.0.0-rc.0",
imagePullPolicy: "pull",
imagePullPolicy: "IfNotPresent",
},
],
serviceAccountName: "dummy",
@ -127,24 +127,24 @@ export const deploymentPod2 = new Pod({
{
name: "init-node-exporter",
image: "docker.io/prom/node-exporter:v1.0.0-rc.0",
imagePullPolicy: "pull",
imagePullPolicy: "IfNotPresent",
},
{
name: "init-node-exporter-1",
image: "docker.io/prom/node-exporter:v1.0.0-rc.0",
imagePullPolicy: "pull",
imagePullPolicy: "IfNotPresent",
},
],
containers: [
{
name: "node-exporter",
image: "docker.io/prom/node-exporter:v1.0.0-rc.0",
imagePullPolicy: "pull",
imagePullPolicy: "IfNotPresent",
},
{
name: "node-exporter-1",
image: "docker.io/prom/node-exporter:v1.0.0-rc.0",
imagePullPolicy: "pull",
imagePullPolicy: "IfNotPresent",
},
],
serviceAccountName: "dummy",
@ -188,12 +188,12 @@ export const deploymentPod3 = new Pod({
{
name: "node-exporter",
image: "docker.io/prom/node-exporter:v1.0.0-rc.0",
imagePullPolicy: "pull",
imagePullPolicy: "IfNotPresent",
},
{
name: "node-exporter-1",
image: "docker.io/prom/node-exporter:v1.0.0-rc.0",
imagePullPolicy: "pull",
imagePullPolicy: "IfNotPresent",
},
],
serviceAccountName: "dummy",

View File

@ -3,13 +3,13 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import type { PodContainer, Pod } from "../../../../common/k8s-api/endpoints";
import type { Container, Pod } from "../../../../common/k8s-api/endpoints";
import type { TabId } from "../dock/store";
import createLogsTabInjectable from "./create-logs-tab.injectable";
export interface PodLogsTabData {
selectedPod: Pod;
selectedContainer: PodContainer;
selectedContainer: Container;
}
const createPodLogsTabInjectable = getInjectable({

View File

@ -12,7 +12,7 @@ import { Badge } from "../../badge";
import type { SelectOption } from "../../select";
import { Select } from "../../select";
import type { LogTabViewModel } from "./logs-view-model";
import type { PodContainer, Pod } from "../../../../common/k8s-api/endpoints";
import type { Container, Pod } from "../../../../common/k8s-api/endpoints";
import type { SingleValue } from "react-select";
export interface LogResourceSelectorProps {
@ -40,7 +40,7 @@ export const LogResourceSelector = observer(({ model }: LogResourceSelectorProps
}));
const allContainers = pod.getAllContainers();
const container = allContainers.find(container => container.name === selectedContainer) ?? null;
const onContainerChange = (option: SingleValue<SelectOption<PodContainer>>) => {
const onContainerChange = (option: SingleValue<SelectOption<Container>>) => {
if (!option) {
return;
}
@ -105,7 +105,7 @@ export const LogResourceSelector = observer(({ model }: LogResourceSelectorProps
menuClass="pod-selector-menu"
/>
<span>Container</span>
<Select<PodContainer, SelectOption<PodContainer>, false>
<Select<Container, SelectOption<Container>, false>
id="container-selector-input"
options={containerSelectOptions}
value={container}