mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
chore: Extract @k8slens/kube-object package
Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
1 parent
576373b16b
commit
1bf24db797
466 files changed
+6838
-6375
No files matched your search
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
extends: "@k8slens/eslint-config/eslint",
|
||||
parserOptions: {
|
||||
project: "./tsconfig.json",
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
"@k8slens/eslint-config/prettier"
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
export * from "./src/api-types";
|
||||
export * from "./src/kube-object";
|
||||
export * from "./src/kube-status";
|
||||
export * from "./src/specifics";
|
||||
export * from "./src/types";
|
||||
export * from "./src/utils";
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require("@k8slens/jest").monorepoPackageConfig(__dirname).configForReact;
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"name": "@k8slens/kube-object",
|
||||
"private": false,
|
||||
"version": "1.0.0-alpha.1",
|
||||
"description": "Injection tokens for list layout",
|
||||
"type": "commonjs",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org/"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/lensapp/lens.git"
|
||||
},
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"author": {
|
||||
"name": "OpenLens Authors",
|
||||
"email": "info@k8slens.dev"
|
||||
},
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/lensapp/lens",
|
||||
"scripts": {
|
||||
"build": "webpack",
|
||||
"clean": "rimraf dist/",
|
||||
"dev": "webpack --mode=development --watch",
|
||||
"test": "jest --coverage --runInBand",
|
||||
"lint": "lens-lint",
|
||||
"lint:fix": "lens-lint --fix"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@k8slens/list-layout": "^1.0.0-alpha.1",
|
||||
"@k8slens/utilities": "^1.0.0-alpha.2",
|
||||
"@ogre-tools/injectable": "^15.1.2",
|
||||
"auto-bind": "^4.0.0",
|
||||
"moment": "^2.29.4",
|
||||
"rfc6902": "^5.0.1",
|
||||
"type-fest": "^2.19.0",
|
||||
"typed-regex": "^0.0.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@k8slens/eslint-config": "^6.5.0-alpha.2",
|
||||
"@k8slens/jest": "^6.5.0-alpha.4",
|
||||
"@k8slens/typescript": "^6.5.0-alpha.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,401 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { KubeObject } from "./kube-object";
|
||||
|
||||
/**
|
||||
* The metadata for LIST requests to the KubeApi
|
||||
*/
|
||||
export interface KubeJsonApiListMetadata {
|
||||
resourceVersion: string;
|
||||
selfLink?: string;
|
||||
}
|
||||
|
||||
export interface KubeJsonApiDataList<T = KubeJsonApiData> {
|
||||
kind: string;
|
||||
apiVersion: string;
|
||||
items: T[];
|
||||
metadata: KubeJsonApiListMetadata;
|
||||
}
|
||||
|
||||
export interface KubeJsonApiData<
|
||||
Metadata extends KubeJsonApiObjectMetadata = KubeJsonApiObjectMetadata,
|
||||
Status = unknown,
|
||||
Spec = unknown,
|
||||
> {
|
||||
kind: string;
|
||||
apiVersion: string;
|
||||
metadata: Metadata;
|
||||
status?: Status;
|
||||
spec?: Spec;
|
||||
[otherKeys: string]: unknown;
|
||||
}
|
||||
|
||||
export type KubeJsonApiDataFor<K> = K extends KubeObject<infer Metadata, infer Status, infer Spec>
|
||||
? KubeJsonApiData<Metadata, Status, Spec>
|
||||
: never;
|
||||
|
||||
export interface KubeObjectConstructorData {
|
||||
readonly kind?: string;
|
||||
readonly namespaced?: boolean;
|
||||
readonly apiBase?: string;
|
||||
}
|
||||
|
||||
export type KubeObjectConstructor<K extends KubeObject, Data> = (new (data: Data) => K) & KubeObjectConstructorData;
|
||||
|
||||
export interface OwnerReference {
|
||||
apiVersion: string;
|
||||
kind: string;
|
||||
name: string;
|
||||
uid: string;
|
||||
controller?: boolean;
|
||||
blockOwnerDeletion?: boolean;
|
||||
}
|
||||
|
||||
export type KubeTemplateObjectMetadata<Namespaced extends KubeObjectScope> = Pick<
|
||||
KubeJsonApiObjectMetadata<KubeObjectScope>,
|
||||
"annotations" | "finalizers" | "generateName" | "labels" | "ownerReferences"
|
||||
> & {
|
||||
name?: string;
|
||||
namespace?: ScopedNamespace<Namespaced>;
|
||||
};
|
||||
|
||||
export interface BaseKubeJsonApiObjectMetadata<Namespaced extends KubeObjectScope> {
|
||||
/**
|
||||
* Annotations is an unstructured key value map stored with a resource that may be set by
|
||||
* external tools to store and retrieve arbitrary metadata. They are not queryable and should be
|
||||
* preserved when modifying objects.
|
||||
*
|
||||
* More info: http://kubernetes.io/docs/user-guide/annotations
|
||||
*/
|
||||
annotations?: Partial<Record<string, string>>;
|
||||
|
||||
/**
|
||||
* The name of the cluster which the object belongs to. This is used to distinguish resources
|
||||
* with same name and namespace in different clusters. This field is not set anywhere right now
|
||||
* and api server is going to ignore it if set in create or update request.
|
||||
*/
|
||||
clusterName?: string;
|
||||
|
||||
/**
|
||||
* CreationTimestamp is a timestamp representing the server time when this object was created. It
|
||||
* is not guaranteed to be set in happens-before order across separate operations. Clients may
|
||||
* not set this value. It is represented in RFC3339 form and is in UTC. Populated by the system.
|
||||
*
|
||||
* More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
|
||||
*/
|
||||
readonly creationTimestamp?: string;
|
||||
|
||||
/**
|
||||
* Number of seconds allowed for this object to gracefully terminate before it will be removed
|
||||
* from the system. Only set when deletionTimestamp is also set. May only be shortened.
|
||||
*/
|
||||
readonly deletionGracePeriodSeconds?: number;
|
||||
|
||||
/**
|
||||
* DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field
|
||||
* is set by the server when a graceful deletion is requested by the user, and is not directly
|
||||
* settable by a client. The resource is expected to be deleted (no longer visible from resource
|
||||
* lists, and not reachable by name) after the time in this field, once the finalizers list is
|
||||
* empty. As long as the finalizers list contains items, deletion is blocked. Once the
|
||||
* `deletionTimestamp` is set, this value may not be unset or be set further into the future,
|
||||
* although it may be shortened or the resource may be deleted prior to this time. For example,
|
||||
* a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a
|
||||
* graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet
|
||||
* will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the
|
||||
* pod from the API. In the presence of network partitions, this object may still exist after
|
||||
* this timestamp, until an administrator or automated process can determine the resource is
|
||||
* fully terminated. If not set, graceful deletion of the object has not been requested.
|
||||
* Populated by the system when a graceful deletion is requested.
|
||||
*
|
||||
* More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
|
||||
*/
|
||||
readonly deletionTimestamp?: string;
|
||||
|
||||
/**
|
||||
* Must be empty before the object is deleted from the registry. Each entry is an identifier for
|
||||
* the responsible component that will remove the entry from the list. If the deletionTimestamp
|
||||
* of the object is non-nil, entries in this list can only be removed. Finalizers may be
|
||||
* processed and removed in any order. Order is NOT enforced because it introduces significant
|
||||
* risk of stuck finalizers. finalizers is a shared field, any actor with permission can reorder
|
||||
* it. If the finalizer list is processed in order, then this can lead to a situation in which
|
||||
* the component responsible for the first finalizer in the list is waiting for a signal (field
|
||||
* value, external system, or other) produced by a component responsible for a finalizer later in
|
||||
* the list, resulting in a deadlock. Without enforced ordering finalizers are free to order
|
||||
* amongst themselves and are not vulnerable to ordering changes in the list.
|
||||
*/
|
||||
finalizers?: string[];
|
||||
|
||||
/**
|
||||
* GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the
|
||||
* Name field has not been provided. If this field is used, the name returned to the client will
|
||||
* be different than the name passed. This value will also be combined with a unique suffix. The
|
||||
* provided value has the same validation rules as the Name field, and may be truncated by the
|
||||
* length of the suffix required to make the value unique on the server. If this field is
|
||||
* specified and the generated name exists, the server will NOT return a 409 - instead, it will
|
||||
* either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not
|
||||
* be found in the time allotted, and the client should retry (optionally after the time indicated
|
||||
* in the Retry-After header). Applied only if Name is not specified.
|
||||
*
|
||||
* More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency
|
||||
*/
|
||||
generateName?: string;
|
||||
|
||||
/**
|
||||
* A sequence number representing a specific generation of the desired state. Populated by the
|
||||
* system.
|
||||
*/
|
||||
readonly generation?: number;
|
||||
|
||||
/**
|
||||
* Map of string keys and values that can be used to organize and categorize (scope and select)
|
||||
* objects. May match selectors of replication controllers and services.
|
||||
*
|
||||
* More info: http://kubernetes.io/docs/user-guide/labels
|
||||
*/
|
||||
labels?: Partial<Record<string, string>>;
|
||||
|
||||
/**
|
||||
* ManagedFields maps workflow-id and version to the set of fields that are managed by that
|
||||
* workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set
|
||||
* or understand this field. A workflow can be the user's name, a controller's name, or the name
|
||||
* of a specific apply path like "ci-cd". The set of fields is always in the version that the
|
||||
* workflow used when modifying the object.
|
||||
*/
|
||||
managedFields?: unknown[];
|
||||
|
||||
/**
|
||||
* Name must be unique within a namespace. Is required when creating resources, although some
|
||||
* resources may allow a client to request the generation of an appropriate name automatically.
|
||||
* Name is primarily intended for creation idempotence and configuration definition.
|
||||
*
|
||||
* More info: http://kubernetes.io/docs/user-guide/identifiers#names
|
||||
*/
|
||||
readonly name: string;
|
||||
|
||||
/**
|
||||
* Namespace defines the space within which each name must be unique. An empty namespace is
|
||||
* equivalent to the "default" namespace, but "default" is the canonical representation. Not all
|
||||
* objects are required to be scoped to a namespace - the value of this field for those objects
|
||||
* will be empty. Must be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces
|
||||
*/
|
||||
readonly namespace?: ScopedNamespace<Namespaced>;
|
||||
|
||||
/**
|
||||
* List of objects depended by this object. If ALL objects in the list have been deleted, this
|
||||
* object will be garbage collected. If this object is managed by a controller, then an entry in
|
||||
* this list will point to this controller, with the controller field set to true. There cannot
|
||||
* be more than one managing controller.
|
||||
*/
|
||||
ownerReferences?: OwnerReference[];
|
||||
|
||||
/**
|
||||
* An opaque value that represents the internal version of this object that can be used by
|
||||
* clients to determine when objects have changed. May be used for optimistic concurrency, change
|
||||
* detection, and the watch operation on a resource or set of resources. Clients must treat these
|
||||
* values as opaque and passed unmodified back to the server. They may only be valid for a
|
||||
* particular resource or set of resources. Populated by the system. Value must be treated as
|
||||
* opaque by clients.
|
||||
*
|
||||
* More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
|
||||
*/
|
||||
readonly resourceVersion?: string;
|
||||
|
||||
/**
|
||||
* SelfLink is a URL representing this object. Populated by the system.
|
||||
*/
|
||||
readonly selfLink?: string;
|
||||
|
||||
/**
|
||||
* UID is the unique in time and space value for this object. It is typically generated by the
|
||||
* server on successful creation of a resource and is not allowed to change on PUT operations.
|
||||
* Populated by the system.
|
||||
*
|
||||
* More info: http://kubernetes.io/docs/user-guide/identifiers#uids
|
||||
*/
|
||||
readonly uid?: string;
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export type KubeJsonApiObjectMetadata<Namespaced extends KubeObjectScope = KubeObjectScope> =
|
||||
BaseKubeJsonApiObjectMetadata<Namespaced> &
|
||||
(Namespaced extends KubeObjectScope.Namespace ? { readonly namespace: string } : {});
|
||||
|
||||
export type KubeObjectMetadata<Namespaced extends KubeObjectScope = KubeObjectScope> =
|
||||
KubeJsonApiObjectMetadata<Namespaced> & {
|
||||
readonly selfLink: string;
|
||||
readonly uid: string;
|
||||
readonly name: string;
|
||||
readonly resourceVersion: string;
|
||||
};
|
||||
|
||||
export type NamespaceScopedMetadata = KubeObjectMetadata<KubeObjectScope.Namespace>;
|
||||
export type ClusterScopedMetadata = KubeObjectMetadata<KubeObjectScope.Cluster>;
|
||||
|
||||
export interface KubeStatusData {
|
||||
kind: string;
|
||||
apiVersion: string;
|
||||
code: number;
|
||||
message?: string;
|
||||
reason?: string;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export interface EvictionObject {
|
||||
kind: "Eviction";
|
||||
apiVersion: string | "policy/v1";
|
||||
metadata: Partial<KubeObjectMetadata>;
|
||||
deleteOptions?: {
|
||||
kind?: string;
|
||||
apiVersion?: string;
|
||||
dryRun?: string[];
|
||||
gracePeriodSeconds?: number;
|
||||
orphanDependents?: boolean;
|
||||
propagationPolicy?: string;
|
||||
preconditions?: {
|
||||
resourceVersion: string;
|
||||
uid: string;
|
||||
}[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface BaseKubeObjectCondition {
|
||||
/**
|
||||
* Last time the condition transit from one status to another.
|
||||
*
|
||||
* @type Date
|
||||
*/
|
||||
lastTransitionTime?: string;
|
||||
/**
|
||||
* A human readable message indicating details about last transition.
|
||||
*/
|
||||
message?: string;
|
||||
/**
|
||||
* brief (usually one word) reason for the condition's last transition.
|
||||
*/
|
||||
reason?: string;
|
||||
/**
|
||||
* Status of the condition
|
||||
*/
|
||||
status: "True" | "False" | "Unknown";
|
||||
/**
|
||||
* Type of the condition
|
||||
*/
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface KubeObjectStatus {
|
||||
conditions?: BaseKubeObjectCondition[];
|
||||
}
|
||||
|
||||
export type KubeMetaField = keyof KubeJsonApiObjectMetadata;
|
||||
|
||||
export class KubeCreationError extends Error {
|
||||
constructor(message: string, public data: unknown) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
export type LabelMatchExpression = {
|
||||
/**
|
||||
* The label key that the selector applies to.
|
||||
*/
|
||||
key: string;
|
||||
} & (
|
||||
| {
|
||||
/**
|
||||
* This represents the key's relationship to a set of values.
|
||||
*/
|
||||
operator: "Exists" | "DoesNotExist";
|
||||
values?: undefined;
|
||||
}
|
||||
| {
|
||||
operator: "In" | "NotIn";
|
||||
/**
|
||||
* The set of values for to match according to the operator for the label.
|
||||
*/
|
||||
values: string[];
|
||||
}
|
||||
);
|
||||
|
||||
export interface Toleration {
|
||||
key?: string;
|
||||
operator?: string;
|
||||
effect?: string;
|
||||
value?: string;
|
||||
tolerationSeconds?: number;
|
||||
}
|
||||
|
||||
export interface ObjectReference {
|
||||
apiVersion?: string;
|
||||
fieldPath?: string;
|
||||
kind?: string;
|
||||
name: string;
|
||||
namespace?: string;
|
||||
resourceVersion?: string;
|
||||
uid?: string;
|
||||
}
|
||||
|
||||
export interface LocalObjectReference {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface TypedLocalObjectReference {
|
||||
apiGroup?: string;
|
||||
kind: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface NodeAffinity {
|
||||
nodeSelectorTerms?: LabelSelector[];
|
||||
weight: number;
|
||||
preference: LabelSelector;
|
||||
}
|
||||
|
||||
export interface PodAffinity {
|
||||
labelSelector: LabelSelector;
|
||||
topologyKey: string;
|
||||
}
|
||||
|
||||
export interface SpecificAffinity<T> {
|
||||
requiredDuringSchedulingIgnoredDuringExecution?: T[];
|
||||
preferredDuringSchedulingIgnoredDuringExecution?: T[];
|
||||
}
|
||||
|
||||
export interface Affinity {
|
||||
nodeAffinity?: SpecificAffinity<NodeAffinity>;
|
||||
podAffinity?: SpecificAffinity<PodAffinity>;
|
||||
podAntiAffinity?: SpecificAffinity<PodAffinity>;
|
||||
}
|
||||
|
||||
export interface LabelSelector {
|
||||
matchLabels?: Partial<Record<string, string>>;
|
||||
matchExpressions?: LabelMatchExpression[];
|
||||
}
|
||||
|
||||
export enum KubeObjectScope {
|
||||
Namespace,
|
||||
Cluster,
|
||||
}
|
||||
|
||||
export type ScopedNamespace<Namespaced extends KubeObjectScope> = Namespaced extends KubeObjectScope.Namespace
|
||||
? string
|
||||
: Namespaced extends KubeObjectScope.Cluster
|
||||
? undefined
|
||||
: string | undefined;
|
||||
|
||||
export interface RawKubeObject<
|
||||
Metadata extends KubeObjectMetadata = KubeObjectMetadata,
|
||||
Status = Record<string, unknown>,
|
||||
Spec = Record<string, unknown>,
|
||||
> {
|
||||
apiVersion: string;
|
||||
kind: string;
|
||||
metadata: Metadata;
|
||||
status?: Status;
|
||||
spec?: Spec;
|
||||
[otherFields: string]: unknown;
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import moment from "moment";
|
||||
import { formatDuration, isObject, isString } from "@k8slens/utilities";
|
||||
import type { ItemObject } from "@k8slens/list-layout";
|
||||
import type { Patch } from "rfc6902";
|
||||
import autoBind from "auto-bind";
|
||||
import type { KubeObjectMetadata, KubeObjectScope, KubeJsonApiData } from "./api-types";
|
||||
import { KubeCreationError } from "./api-types";
|
||||
import {
|
||||
isKubeObjectNonSystem,
|
||||
isJsonApiData,
|
||||
isKubeJsonApiListMetadata,
|
||||
isKubeJsonApiMetadata,
|
||||
isPartialJsonApiMetadata,
|
||||
isPartialJsonApiData,
|
||||
isJsonApiDataList,
|
||||
stringifyLabels,
|
||||
filterOutResourceApplierAnnotations,
|
||||
} from "./utils";
|
||||
|
||||
export function createKubeObject<
|
||||
Metadata extends KubeObjectMetadata = KubeObjectMetadata,
|
||||
Status = unknown,
|
||||
Spec = unknown,
|
||||
>(data: KubeJsonApiData<Metadata, Status, Spec>) {
|
||||
return new KubeObject(data);
|
||||
}
|
||||
|
||||
export class KubeObject<
|
||||
Metadata extends KubeObjectMetadata<KubeObjectScope> = KubeObjectMetadata<KubeObjectScope>,
|
||||
Status = unknown,
|
||||
Spec = unknown,
|
||||
> implements ItemObject
|
||||
{
|
||||
static readonly kind?: string;
|
||||
|
||||
static readonly namespaced?: boolean;
|
||||
|
||||
static readonly apiBase?: string;
|
||||
|
||||
apiVersion!: string;
|
||||
|
||||
kind!: string;
|
||||
|
||||
metadata!: Metadata;
|
||||
|
||||
status?: Status;
|
||||
|
||||
spec!: Spec;
|
||||
|
||||
/**
|
||||
* @deprecated Switch to using {@link createKubeObject} instead
|
||||
*/
|
||||
static create = createKubeObject;
|
||||
|
||||
/**
|
||||
* @deprecated Switch to using {@link isKubeObjectNonSystem} instead
|
||||
*/
|
||||
static isNonSystem = isKubeObjectNonSystem;
|
||||
|
||||
/**
|
||||
* @deprecated Switch to using {@link isJsonApiData} instead
|
||||
*/
|
||||
static isJsonApiData = isJsonApiData;
|
||||
|
||||
/**
|
||||
* @deprecated Switch to using {@link isKubeJsonApiListMetadata} instead
|
||||
*/
|
||||
static isKubeJsonApiListMetadata = isKubeJsonApiListMetadata;
|
||||
|
||||
/**
|
||||
* @deprecated Switch to using {@link isKubeJsonApiMetadata} instead
|
||||
*/
|
||||
static isKubeJsonApiMetadata = isKubeJsonApiMetadata;
|
||||
|
||||
/**
|
||||
* @deprecated Switch to using {@link isPartialJsonApiMetadata} instead
|
||||
*/
|
||||
static isPartialJsonApiMetadata = isPartialJsonApiMetadata;
|
||||
|
||||
/**
|
||||
* @deprecated Switch to using {@link isPartialJsonApiData} instead
|
||||
*/
|
||||
static isPartialJsonApiData = isPartialJsonApiData;
|
||||
|
||||
/**
|
||||
* @deprecated Switch to using {@link isJsonApiDataList} instead
|
||||
*/
|
||||
static isJsonApiDataList = isJsonApiDataList;
|
||||
|
||||
/**
|
||||
* @deprecated Switch to using {@link stringifyLabels} instead
|
||||
*/
|
||||
static stringifyLabels = stringifyLabels;
|
||||
|
||||
constructor(data: KubeJsonApiData<Metadata, Status, Spec>) {
|
||||
if (!isObject(data)) {
|
||||
throw new TypeError(`Cannot create a KubeObject from ${typeof data}`);
|
||||
}
|
||||
|
||||
if (!isObject(data.metadata)) {
|
||||
throw new KubeCreationError(`Cannot create a KubeObject from an object without metadata`, data);
|
||||
}
|
||||
|
||||
if (!isString(data.metadata.name)) {
|
||||
throw new KubeCreationError(
|
||||
`Cannot create a KubeObject from an object without metadata.name being a string`,
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
if (!isString(data.metadata.uid)) {
|
||||
throw new KubeCreationError(
|
||||
`Cannot create a KubeObject from an object without metadata.uid being a string`,
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
if (!isString(data.metadata.resourceVersion)) {
|
||||
throw new KubeCreationError(
|
||||
`Cannot create a KubeObject from an object without metadata.resourceVersion being a string`,
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
if (!isString(data.metadata.selfLink)) {
|
||||
throw new KubeCreationError(
|
||||
`Cannot create a KubeObject from an object without metadata.selfLink being a string`,
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
Object.assign(this, data);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
get selfLink() {
|
||||
return this.metadata.selfLink;
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.metadata.uid;
|
||||
}
|
||||
|
||||
getResourceVersion() {
|
||||
return this.metadata.resourceVersion;
|
||||
}
|
||||
|
||||
getScopedName() {
|
||||
return [this.getNs(), this.getName()].filter(Boolean).join("/");
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.metadata.name;
|
||||
}
|
||||
|
||||
getNs(): Metadata["namespace"] {
|
||||
// avoid "null" serialization via JSON.stringify when post data
|
||||
return this.metadata.namespace || undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function computes the number of milliseconds from the UNIX EPOCH to the
|
||||
* creation timestamp of this object.
|
||||
*/
|
||||
getCreationTimestamp() {
|
||||
if (!this.metadata.creationTimestamp) {
|
||||
return Date.now();
|
||||
}
|
||||
|
||||
return new Date(this.metadata.creationTimestamp).getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This function computes a new "now". Switch to using {@link KubeObject.getCreationTimestamp} instead
|
||||
*/
|
||||
getTimeDiffFromNow(): number {
|
||||
if (!this.metadata.creationTimestamp) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Date.now() - new Date(this.metadata.creationTimestamp).getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This function computes a new "now" on every call might cause subtle issues if called multiple times
|
||||
*
|
||||
* NOTE: this function also is not reactive to updates in the current time so it should not be used for rendering
|
||||
*/
|
||||
getAge(humanize = true, compact = true, fromNow = false): string | number {
|
||||
if (fromNow) {
|
||||
return moment(this.metadata.creationTimestamp).fromNow(); // "string", getTimeDiffFromNow() cannot be used
|
||||
}
|
||||
const diff = this.getTimeDiffFromNow();
|
||||
|
||||
if (humanize) {
|
||||
return formatDuration(diff, compact);
|
||||
}
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
getFinalizers(): string[] {
|
||||
return this.metadata.finalizers || [];
|
||||
}
|
||||
|
||||
getLabels(): string[] {
|
||||
return KubeObject.stringifyLabels(this.metadata.labels);
|
||||
}
|
||||
|
||||
getAnnotations(filter = false): string[] {
|
||||
const labels = KubeObject.stringifyLabels(this.metadata.annotations);
|
||||
|
||||
if (!filter) {
|
||||
return labels;
|
||||
}
|
||||
|
||||
return labels.filter(filterOutResourceApplierAnnotations);
|
||||
}
|
||||
|
||||
getOwnerRefs() {
|
||||
const refs = this.metadata.ownerReferences || [];
|
||||
const namespace = this.getNs();
|
||||
|
||||
return refs.map((ownerRef) => ({ ...ownerRef, namespace }));
|
||||
}
|
||||
|
||||
getSearchFields() {
|
||||
return [this.getName(), this.getNs(), this.getId(), ...this.getLabels(), ...this.getAnnotations(true)];
|
||||
}
|
||||
|
||||
toPlainObject() {
|
||||
return JSON.parse(JSON.stringify(this)) as Record<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use KubeApi.patch instead
|
||||
*/
|
||||
async patch(patch: Patch): Promise<KubeJsonApiData | null> {
|
||||
void patch;
|
||||
|
||||
throw new Error("KubeObject.patch() has been deprecated since v5.3.0, please switch to using KubeApi.patch()");
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use KubeApi.patch instead
|
||||
*/
|
||||
async update(data: Partial<this>): Promise<KubeJsonApiData | null> {
|
||||
void data;
|
||||
|
||||
throw new Error("KubeObject.update() has been deprecated since v5.3.0, please switch to using KubeApi.patch()");
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use KubeApi.delete instead
|
||||
*/
|
||||
delete(params?: object) {
|
||||
void params;
|
||||
|
||||
throw new Error("KubeObject.delete() has been deprecated since v5.3.0, please switch to using KubeApi.delete()");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { isObject, hasTypedProperty, hasOptionalTypedProperty, isString, isNumber } from "@k8slens/utilities";
|
||||
import type { KubeStatusData } from "./api-types";
|
||||
|
||||
/**
|
||||
* Is the shape of {@link object} correct for {@link KubeStatusData}
|
||||
* @param object Some object
|
||||
* @returns
|
||||
*/
|
||||
export function isKubeStatusData(object: unknown): object is KubeStatusData {
|
||||
return (
|
||||
isObject(object) &&
|
||||
hasTypedProperty(object, "kind", isString) &&
|
||||
hasTypedProperty(object, "apiVersion", isString) &&
|
||||
hasTypedProperty(object, "code", isNumber) &&
|
||||
(hasOptionalTypedProperty(object, "message", isString) ||
|
||||
hasOptionalTypedProperty(object, "reason", isString) ||
|
||||
hasOptionalTypedProperty(object, "status", isString)) &&
|
||||
object.kind === "Status"
|
||||
);
|
||||
}
|
||||
|
||||
export class KubeStatus {
|
||||
public readonly kind = "Status";
|
||||
|
||||
public readonly apiVersion: string;
|
||||
|
||||
public readonly code: number;
|
||||
|
||||
public readonly message: string;
|
||||
|
||||
public readonly reason: string;
|
||||
|
||||
public readonly status: string;
|
||||
|
||||
constructor(data: KubeStatusData) {
|
||||
this.apiVersion = data.apiVersion;
|
||||
this.code = data.code;
|
||||
this.message = data.message || "";
|
||||
this.reason = data.reason || "";
|
||||
this.status = data.status || "";
|
||||
}
|
||||
|
||||
getExplanation(): string {
|
||||
const { code, message, reason, status } = this;
|
||||
|
||||
return `${code}: ${message || reason || status}`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { KubeJsonApiData, KubeObjectMetadata, KubeObjectScope, ClusterScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import type { RoleRef } from "../types/role-ref";
|
||||
import type { Subject } from "../types/subject";
|
||||
|
||||
export interface ClusterRoleBindingData
|
||||
extends KubeJsonApiData<KubeObjectMetadata<KubeObjectScope.Cluster>, void, void> {
|
||||
subjects?: Subject[];
|
||||
roleRef: RoleRef;
|
||||
}
|
||||
|
||||
export class ClusterRoleBinding extends KubeObject<ClusterScopedMetadata, void, void> {
|
||||
static kind = "ClusterRoleBinding";
|
||||
|
||||
static namespaced = false;
|
||||
|
||||
static apiBase = "/apis/rbac.authorization.k8s.io/v1/clusterrolebindings";
|
||||
|
||||
subjects?: Subject[];
|
||||
|
||||
roleRef: RoleRef;
|
||||
|
||||
constructor({ subjects, roleRef, ...rest }: ClusterRoleBindingData) {
|
||||
super(rest);
|
||||
this.subjects = subjects;
|
||||
this.roleRef = roleRef;
|
||||
}
|
||||
|
||||
getSubjects() {
|
||||
return this.subjects ?? [];
|
||||
}
|
||||
|
||||
getSubjectNames(): string {
|
||||
return this.getSubjects()
|
||||
.map((subject) => subject.name)
|
||||
.join(", ");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { KubeJsonApiData, KubeObjectMetadata, KubeObjectScope, ClusterScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import type { AggregationRule } from "../types/aggregation-rule";
|
||||
import type { PolicyRule } from "../types/policy-rule";
|
||||
|
||||
export interface ClusterRoleData extends KubeJsonApiData<KubeObjectMetadata<KubeObjectScope.Cluster>, void, void> {
|
||||
rules?: PolicyRule[];
|
||||
aggregationRule?: AggregationRule;
|
||||
}
|
||||
|
||||
export class ClusterRole extends KubeObject<ClusterScopedMetadata, void, void> {
|
||||
static kind = "ClusterRole";
|
||||
|
||||
static namespaced = false;
|
||||
|
||||
static apiBase = "/apis/rbac.authorization.k8s.io/v1/clusterroles";
|
||||
|
||||
rules?: PolicyRule[];
|
||||
|
||||
aggregationRule?: AggregationRule;
|
||||
|
||||
constructor({ rules, aggregationRule, ...rest }: ClusterRoleData) {
|
||||
super(rest);
|
||||
this.rules = rules;
|
||||
this.aggregationRule = aggregationRule;
|
||||
}
|
||||
|
||||
getRules() {
|
||||
return this.rules || [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { KubeObject } from "../kube-object";
|
||||
|
||||
export enum ClusterStatus {
|
||||
ACTIVE = "Active",
|
||||
CREATING = "Creating",
|
||||
REMOVING = "Removing",
|
||||
ERROR = "Error",
|
||||
}
|
||||
|
||||
export interface Cluster {
|
||||
spec: {
|
||||
clusterNetwork?: {
|
||||
serviceDomain?: string;
|
||||
pods?: {
|
||||
cidrBlocks?: string[];
|
||||
};
|
||||
services?: {
|
||||
cidrBlocks?: string[];
|
||||
};
|
||||
};
|
||||
providerSpec: {
|
||||
value: {
|
||||
profile: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
status?: {
|
||||
apiEndpoints: {
|
||||
host: string;
|
||||
port: string;
|
||||
}[];
|
||||
providerStatus: {
|
||||
adminUser?: string;
|
||||
adminPassword?: string;
|
||||
kubeconfig?: string;
|
||||
processState?: string;
|
||||
lensAddress?: string;
|
||||
};
|
||||
errorMessage?: string;
|
||||
errorReason?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export class Cluster extends KubeObject {
|
||||
static kind = "Cluster";
|
||||
|
||||
static apiBase = "/apis/cluster.k8s.io/v1alpha1/clusters";
|
||||
|
||||
static namespaced = true;
|
||||
|
||||
getStatus() {
|
||||
if (this.metadata.deletionTimestamp) {
|
||||
return ClusterStatus.REMOVING;
|
||||
}
|
||||
|
||||
if (!this.status || !this.status) {
|
||||
return ClusterStatus.CREATING;
|
||||
}
|
||||
|
||||
if (this.status.errorMessage) {
|
||||
return ClusterStatus.ERROR;
|
||||
}
|
||||
|
||||
return ClusterStatus.ACTIVE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { KubeObject } from "../kube-object";
|
||||
|
||||
export interface ComponentStatusCondition {
|
||||
type: string;
|
||||
status: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface ComponentStatus {
|
||||
conditions: ComponentStatusCondition[];
|
||||
}
|
||||
|
||||
export class ComponentStatus extends KubeObject {
|
||||
static kind = "ComponentStatus";
|
||||
|
||||
static namespaced = false;
|
||||
|
||||
static apiBase = "/api/v1/componentstatuses";
|
||||
|
||||
getTruthyConditions() {
|
||||
return this.conditions.filter((c) => c.status === "True");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import autoBind from "auto-bind";
|
||||
import type { KubeJsonApiData, KubeObjectMetadata, KubeObjectScope, NamespaceScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
|
||||
export interface ConfigMapData extends KubeJsonApiData<KubeObjectMetadata<KubeObjectScope.Namespace>, void, void> {
|
||||
data?: Partial<Record<string, string>>;
|
||||
binaryData?: Partial<Record<string, string>>;
|
||||
immutable?: boolean;
|
||||
}
|
||||
|
||||
export class ConfigMap extends KubeObject<NamespaceScopedMetadata, void, void> {
|
||||
static kind = "ConfigMap";
|
||||
|
||||
static namespaced = true;
|
||||
|
||||
static apiBase = "/api/v1/configmaps";
|
||||
|
||||
data: Partial<Record<string, string>>;
|
||||
|
||||
binaryData: Partial<Record<string, string>>;
|
||||
|
||||
immutable?: boolean;
|
||||
|
||||
constructor({ data, binaryData, immutable, ...rest }: ConfigMapData) {
|
||||
super(rest);
|
||||
autoBind(this);
|
||||
|
||||
this.data = data ?? {};
|
||||
this.binaryData = binaryData ?? {};
|
||||
this.immutable = immutable;
|
||||
}
|
||||
|
||||
getKeys(): string[] {
|
||||
return Object.keys(this.data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { formatDuration } from "@k8slens/utilities";
|
||||
import moment from "moment";
|
||||
import type { ObjectReference, NamespaceScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import type { JobTemplateSpec } from "../types/job-template-spec";
|
||||
|
||||
export interface CronJobSpec {
|
||||
concurrencyPolicy?: string;
|
||||
failedJobsHistoryLimit?: number;
|
||||
jobTemplate?: JobTemplateSpec;
|
||||
schedule: string;
|
||||
startingDeadlineSeconds?: number;
|
||||
successfulJobsHistoryLimit?: number;
|
||||
suspend?: boolean;
|
||||
}
|
||||
|
||||
export interface CronJobStatus {
|
||||
lastScheduleTime?: string;
|
||||
lastSuccessfulTime?: string;
|
||||
active?: ObjectReference[];
|
||||
}
|
||||
|
||||
export class CronJob extends KubeObject<NamespaceScopedMetadata, CronJobStatus, CronJobSpec> {
|
||||
static readonly kind = "CronJob";
|
||||
|
||||
static readonly namespaced = true;
|
||||
|
||||
static readonly apiBase = "/apis/batch/v1/cronjobs";
|
||||
|
||||
getSuspendFlag() {
|
||||
return (this.spec.suspend ?? false).toString();
|
||||
}
|
||||
|
||||
getLastScheduleTime() {
|
||||
if (!this.status?.lastScheduleTime) {
|
||||
return "-";
|
||||
}
|
||||
const diff = moment().diff(this.status.lastScheduleTime);
|
||||
|
||||
return formatDuration(diff, true);
|
||||
}
|
||||
|
||||
getSchedule() {
|
||||
return this.spec.schedule;
|
||||
}
|
||||
|
||||
isNeverRun() {
|
||||
const schedule = this.getSchedule();
|
||||
const daysInMonth = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
||||
const stamps = schedule.split(" ");
|
||||
const day = Number(stamps[stamps.length - 3]); // 1-31
|
||||
const month = Number(stamps[stamps.length - 2]); // 1-12
|
||||
|
||||
if (schedule.startsWith("@")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return day > daysInMonth[month - 1];
|
||||
}
|
||||
|
||||
isSuspend() {
|
||||
return this.spec.suspend;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { buildURL } from "@k8slens/utilities";
|
||||
import type { BaseKubeObjectCondition, ClusterScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import type { JSONSchemaProps } from "../types/json-schema-props";
|
||||
import type { WebhookClientConfig } from "./mutating-webhook-configuration";
|
||||
|
||||
export interface AdditionalPrinterColumnsCommon {
|
||||
name: string;
|
||||
type: "integer" | "number" | "string" | "boolean" | "date";
|
||||
priority?: number;
|
||||
format?: "int32" | "int64" | "float" | "double" | "byte" | "binary" | "date" | "date-time" | "password";
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export type AdditionalPrinterColumnsV1 = AdditionalPrinterColumnsCommon & {
|
||||
jsonPath: string;
|
||||
};
|
||||
|
||||
type AdditionalPrinterColumnsV1Beta = AdditionalPrinterColumnsCommon & {
|
||||
JSONPath: string;
|
||||
};
|
||||
|
||||
export interface CustomResourceValidation {
|
||||
openAPIV3Schema?: JSONSchemaProps;
|
||||
}
|
||||
|
||||
export interface CustomResourceDefinitionVersion {
|
||||
name: string;
|
||||
served: boolean;
|
||||
storage: boolean;
|
||||
schema?: CustomResourceValidation; // required in v1 but not present in v1beta
|
||||
additionalPrinterColumns?: AdditionalPrinterColumnsV1[];
|
||||
}
|
||||
|
||||
export interface CustomResourceDefinitionNames {
|
||||
categories?: string[];
|
||||
kind: string;
|
||||
listKind?: string;
|
||||
plural: string;
|
||||
shortNames?: string[];
|
||||
singular?: string;
|
||||
}
|
||||
|
||||
export interface CustomResourceConversion {
|
||||
strategy?: string;
|
||||
webhook?: WebhookConversion;
|
||||
}
|
||||
|
||||
export interface WebhookConversion {
|
||||
clientConfig?: WebhookClientConfig[];
|
||||
conversionReviewVersions: string[];
|
||||
}
|
||||
|
||||
export interface CustomResourceDefinitionSpec {
|
||||
group: string;
|
||||
/**
|
||||
* @deprecated for apiextensions.k8s.io/v1 but used in v1beta1
|
||||
*/
|
||||
version?: string;
|
||||
names: CustomResourceDefinitionNames;
|
||||
scope: "Namespaced" | "Cluster";
|
||||
/**
|
||||
* @deprecated for apiextensions.k8s.io/v1 but used in v1beta1
|
||||
*/
|
||||
validation?: object;
|
||||
versions?: CustomResourceDefinitionVersion[];
|
||||
conversion?: CustomResourceConversion;
|
||||
/**
|
||||
* @deprecated for apiextensions.k8s.io/v1 but used in v1beta1
|
||||
*/
|
||||
additionalPrinterColumns?: AdditionalPrinterColumnsV1Beta[];
|
||||
preserveUnknownFields?: boolean;
|
||||
}
|
||||
|
||||
export interface CustomResourceDefinitionConditionAcceptedNames {
|
||||
plural: string;
|
||||
singular: string;
|
||||
kind: string;
|
||||
shortNames: string[];
|
||||
listKind: string;
|
||||
}
|
||||
|
||||
export interface CustomResourceDefinitionStatus {
|
||||
conditions?: BaseKubeObjectCondition[];
|
||||
acceptedNames: CustomResourceDefinitionConditionAcceptedNames;
|
||||
storedVersions: string[];
|
||||
}
|
||||
|
||||
export class CustomResourceDefinition extends KubeObject<
|
||||
ClusterScopedMetadata,
|
||||
CustomResourceDefinitionStatus,
|
||||
CustomResourceDefinitionSpec
|
||||
> {
|
||||
static kind = "CustomResourceDefinition";
|
||||
|
||||
static namespaced = false;
|
||||
|
||||
static apiBase = "/apis/apiextensions.k8s.io/v1/customresourcedefinitions";
|
||||
|
||||
getResourceUrl() {
|
||||
// TODO: replace this magic string with a use of `customResourcesRouteInjectable` when that is extracted
|
||||
return buildURL("/crd/:group?/:name?", {
|
||||
params: {
|
||||
group: this.getGroup(),
|
||||
name: this.getPluralName(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
getResourceApiBase() {
|
||||
const { group } = this.spec;
|
||||
|
||||
return `/apis/${group}/${this.getVersion()}/${this.getPluralName()}`;
|
||||
}
|
||||
|
||||
getPluralName() {
|
||||
return this.getNames().plural;
|
||||
}
|
||||
|
||||
getResourceKind() {
|
||||
return this.spec.names.kind;
|
||||
}
|
||||
|
||||
getResourceTitle() {
|
||||
const name = this.getPluralName();
|
||||
|
||||
return name[0].toUpperCase() + name.slice(1);
|
||||
}
|
||||
|
||||
getGroup() {
|
||||
return this.spec.group;
|
||||
}
|
||||
|
||||
getScope() {
|
||||
return this.spec.scope;
|
||||
}
|
||||
|
||||
getPreferedVersion(): CustomResourceDefinitionVersion {
|
||||
const { apiVersion } = this;
|
||||
|
||||
switch (apiVersion) {
|
||||
case "apiextensions.k8s.io/v1":
|
||||
for (const version of this.spec.versions ?? []) {
|
||||
if (version.storage) {
|
||||
return version;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "apiextensions.k8s.io/v1beta1": {
|
||||
const { additionalPrinterColumns: apc } = this.spec;
|
||||
const additionalPrinterColumns = apc?.map(({ JSONPath, ...apc }) => ({ ...apc, jsonPath: JSONPath }));
|
||||
|
||||
return {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
name: this.spec.version!,
|
||||
served: true,
|
||||
storage: true,
|
||||
schema: this.spec.validation,
|
||||
additionalPrinterColumns,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Unknown apiVersion=${apiVersion}: Failed to find a version for CustomResourceDefinition ${this.metadata.name}`,
|
||||
);
|
||||
}
|
||||
|
||||
getVersion() {
|
||||
return this.getPreferedVersion().name;
|
||||
}
|
||||
|
||||
isNamespaced() {
|
||||
return this.getScope() === "Namespaced";
|
||||
}
|
||||
|
||||
getStoredVersions() {
|
||||
return this.status?.storedVersions.join(", ") ?? "";
|
||||
}
|
||||
|
||||
getNames() {
|
||||
return this.spec.names;
|
||||
}
|
||||
|
||||
getConversion() {
|
||||
return JSON.stringify(this.spec.conversion);
|
||||
}
|
||||
|
||||
getPrinterColumns(ignorePriority = true): AdditionalPrinterColumnsV1[] {
|
||||
const columns = this.getPreferedVersion().additionalPrinterColumns ?? [];
|
||||
|
||||
return columns.filter((column) => column.name.toLowerCase() !== "age" && (ignorePriority || !column.priority));
|
||||
}
|
||||
|
||||
getValidation() {
|
||||
return JSON.stringify(this.getPreferedVersion().schema, null, 2);
|
||||
}
|
||||
|
||||
getConditions() {
|
||||
if (!this.status?.conditions) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return this.status.conditions.map((condition) => {
|
||||
const { message, reason, lastTransitionTime, status } = condition;
|
||||
|
||||
return {
|
||||
...condition,
|
||||
isReady: status === "True",
|
||||
tooltip: `${message || reason} (${lastTransitionTime})`,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { LabelSelector, KubeObjectStatus, NamespaceScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import type { PodTemplateSpec } from "../types/pod-template-spec";
|
||||
|
||||
export interface RollingUpdateDaemonSet {
|
||||
maxUnavailable?: number | string;
|
||||
maxSurge?: number | string;
|
||||
}
|
||||
|
||||
export interface DaemonSetUpdateStrategy {
|
||||
type: string;
|
||||
rollingUpdate: RollingUpdateDaemonSet;
|
||||
}
|
||||
|
||||
export interface DaemonSetSpec {
|
||||
selector: LabelSelector;
|
||||
template: PodTemplateSpec;
|
||||
updateStrategy: DaemonSetUpdateStrategy;
|
||||
minReadySeconds?: number;
|
||||
revisionHistoryLimit?: number;
|
||||
}
|
||||
|
||||
export interface DaemonSetStatus extends KubeObjectStatus {
|
||||
collisionCount?: number;
|
||||
currentNumberScheduled: number;
|
||||
desiredNumberScheduled: number;
|
||||
numberAvailable?: number;
|
||||
numberMisscheduled: number;
|
||||
numberReady: number;
|
||||
numberUnavailable?: number;
|
||||
observedGeneration?: number;
|
||||
updatedNumberScheduled?: number;
|
||||
}
|
||||
|
||||
export class DaemonSet extends KubeObject<NamespaceScopedMetadata, DaemonSetStatus, DaemonSetSpec> {
|
||||
static kind = "DaemonSet";
|
||||
|
||||
static namespaced = true;
|
||||
|
||||
static apiBase = "/apis/apps/v1/daemonsets";
|
||||
|
||||
getSelectors(): string[] {
|
||||
return KubeObject.stringifyLabels(this.spec.selector.matchLabels);
|
||||
}
|
||||
|
||||
getNodeSelectors(): string[] {
|
||||
return KubeObject.stringifyLabels(this.spec.template.spec?.nodeSelector);
|
||||
}
|
||||
|
||||
getTemplateLabels(): string[] {
|
||||
return KubeObject.stringifyLabels(this.spec.template.metadata?.labels);
|
||||
}
|
||||
|
||||
getTolerations() {
|
||||
return this.spec.template.spec?.tolerations ?? [];
|
||||
}
|
||||
|
||||
getAffinity() {
|
||||
return this.spec.template.spec?.affinity;
|
||||
}
|
||||
|
||||
getAffinityNumber() {
|
||||
return Object.keys(this.getAffinity() ?? {}).length;
|
||||
}
|
||||
|
||||
getImages() {
|
||||
const containers = this.spec.template?.spec?.containers ?? [];
|
||||
const initContainers = this.spec.template?.spec?.initContainers ?? [];
|
||||
|
||||
return [...containers, ...initContainers].map((container) => container.image);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { LabelSelector, KubeObjectStatus, NamespaceScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import type { PodSpec } from "./pod";
|
||||
|
||||
export interface DeploymentSpec {
|
||||
replicas: number;
|
||||
selector: LabelSelector;
|
||||
template: {
|
||||
metadata: {
|
||||
creationTimestamp?: string;
|
||||
labels: Partial<Record<string, string>>;
|
||||
annotations?: Partial<Record<string, string>>;
|
||||
};
|
||||
spec: PodSpec;
|
||||
};
|
||||
strategy: {
|
||||
type: string;
|
||||
rollingUpdate: {
|
||||
maxUnavailable: number;
|
||||
maxSurge: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface DeploymentStatus extends KubeObjectStatus {
|
||||
observedGeneration: number;
|
||||
replicas: number;
|
||||
updatedReplicas: number;
|
||||
readyReplicas: number;
|
||||
availableReplicas?: number;
|
||||
unavailableReplicas?: number;
|
||||
}
|
||||
|
||||
export class Deployment extends KubeObject<NamespaceScopedMetadata, DeploymentStatus, DeploymentSpec> {
|
||||
static kind = "Deployment";
|
||||
|
||||
static namespaced = true;
|
||||
|
||||
static apiBase = "/apis/apps/v1/deployments";
|
||||
|
||||
getSelectors(): string[] {
|
||||
return KubeObject.stringifyLabels(this.spec.selector.matchLabels);
|
||||
}
|
||||
|
||||
getNodeSelectors(): string[] {
|
||||
return KubeObject.stringifyLabels(this.spec.template.spec.nodeSelector);
|
||||
}
|
||||
|
||||
getTemplateLabels(): string[] {
|
||||
return KubeObject.stringifyLabels(this.spec.template.metadata.labels);
|
||||
}
|
||||
|
||||
getTolerations() {
|
||||
return this.spec.template.spec.tolerations ?? [];
|
||||
}
|
||||
|
||||
getAffinity() {
|
||||
return this.spec.template.spec.affinity;
|
||||
}
|
||||
|
||||
getAffinityNumber() {
|
||||
return Object.keys(this.getAffinity() ?? {}).length;
|
||||
}
|
||||
|
||||
getConditions(activeOnly = false) {
|
||||
const { conditions = [] } = this.status ?? {};
|
||||
|
||||
if (activeOnly) {
|
||||
return conditions.filter((c) => c.status === "True");
|
||||
}
|
||||
|
||||
return conditions;
|
||||
}
|
||||
|
||||
getConditionsText(activeOnly = true) {
|
||||
return this.getConditions(activeOnly)
|
||||
.map(({ type }) => type)
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
getReplicas() {
|
||||
return this.spec.replicas || 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import autoBind from "auto-bind";
|
||||
import type {
|
||||
ObjectReference,
|
||||
KubeJsonApiData,
|
||||
KubeObjectMetadata,
|
||||
KubeObjectScope,
|
||||
NamespaceScopedMetadata,
|
||||
} from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
|
||||
export function formatEndpointSubset(subset: EndpointSubset): string {
|
||||
const { addresses, ports } = subset;
|
||||
|
||||
if (!addresses || !ports) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return addresses.map((address) => ports.map((port) => `${address.ip}:${port.port}`).join(", ")).join(", ");
|
||||
}
|
||||
|
||||
export interface ForZone {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface EndpointHints {
|
||||
forZones?: ForZone[];
|
||||
}
|
||||
|
||||
export interface EndpointConditions {
|
||||
ready?: boolean;
|
||||
serving?: boolean;
|
||||
terminating?: boolean;
|
||||
}
|
||||
|
||||
export interface EndpointData {
|
||||
addresses: string[];
|
||||
conditions?: EndpointConditions;
|
||||
hints?: EndpointHints;
|
||||
hostname?: string;
|
||||
nodeName?: string;
|
||||
targetRef?: ObjectReference;
|
||||
zone?: string;
|
||||
}
|
||||
|
||||
export interface EndpointPort {
|
||||
appProtocol?: string;
|
||||
name?: string;
|
||||
protocol?: string;
|
||||
port: number;
|
||||
}
|
||||
|
||||
export interface EndpointAddress {
|
||||
hostname?: string;
|
||||
ip: string;
|
||||
nodeName?: string;
|
||||
targetRef?: ObjectReference;
|
||||
}
|
||||
|
||||
export interface EndpointSubset {
|
||||
addresses?: EndpointAddress[];
|
||||
notReadyAddresses?: EndpointAddress[];
|
||||
ports?: EndpointPort[];
|
||||
}
|
||||
|
||||
export interface EndpointsData extends KubeJsonApiData<KubeObjectMetadata<KubeObjectScope.Namespace>, void, void> {
|
||||
subsets?: EndpointSubset[];
|
||||
}
|
||||
|
||||
export class Endpoints extends KubeObject<NamespaceScopedMetadata, void, void> {
|
||||
static kind = "Endpoints";
|
||||
|
||||
static namespaced = true;
|
||||
|
||||
static apiBase = "/api/v1/endpoints";
|
||||
|
||||
subsets?: EndpointSubset[];
|
||||
|
||||
constructor({ subsets, ...rest }: EndpointsData) {
|
||||
super(rest);
|
||||
autoBind(this);
|
||||
this.subsets = subsets;
|
||||
}
|
||||
|
||||
getEndpointSubsets(): Required<EndpointSubset>[] {
|
||||
return (
|
||||
this.subsets?.map(({ addresses = [], notReadyAddresses = [], ports = [] }) => ({
|
||||
addresses,
|
||||
notReadyAddresses,
|
||||
ports,
|
||||
})) ?? []
|
||||
);
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return this.getEndpointSubsets().map(formatEndpointSubset).join(", ") || "<none>";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { formatDuration } from "@k8slens/utilities";
|
||||
import moment from "moment";
|
||||
import type { KubeJsonApiData, KubeObjectMetadata, KubeObjectScope, ObjectReference } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
|
||||
export interface EventSeries {
|
||||
count?: number;
|
||||
lastObservedTime?: string;
|
||||
}
|
||||
|
||||
export interface EventSource {
|
||||
component?: string;
|
||||
host?: string;
|
||||
}
|
||||
|
||||
export interface KubeEventData extends KubeJsonApiData<KubeObjectMetadata<KubeObjectScope.Namespace>, void, void> {
|
||||
action?: string;
|
||||
count?: number;
|
||||
eventTime?: string;
|
||||
firstTimestamp?: string;
|
||||
involvedObject: Required<ObjectReference>;
|
||||
lastTimestamp?: string;
|
||||
message?: string;
|
||||
reason?: string;
|
||||
related?: ObjectReference;
|
||||
reportingComponent?: string;
|
||||
reportingInstance?: string;
|
||||
series?: EventSeries;
|
||||
source?: EventSource;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
export class KubeEvent extends KubeObject<KubeObjectMetadata<KubeObjectScope.Namespace>, void, void> {
|
||||
static kind = "Event";
|
||||
|
||||
static namespaced = true;
|
||||
|
||||
static apiBase = "/api/v1/events";
|
||||
|
||||
action?: string;
|
||||
|
||||
count?: number;
|
||||
|
||||
eventTime?: string;
|
||||
|
||||
firstTimestamp?: string;
|
||||
|
||||
involvedObject: Required<ObjectReference>;
|
||||
|
||||
lastTimestamp?: string;
|
||||
|
||||
message?: string;
|
||||
|
||||
reason?: string;
|
||||
|
||||
related?: ObjectReference;
|
||||
|
||||
reportingComponent?: string;
|
||||
|
||||
reportingInstance?: string;
|
||||
|
||||
series?: EventSeries;
|
||||
|
||||
source?: EventSource;
|
||||
|
||||
/**
|
||||
* Current supported values are:
|
||||
* - "Normal"
|
||||
* - "Warning"
|
||||
*/
|
||||
type?: string;
|
||||
|
||||
constructor({
|
||||
action,
|
||||
count,
|
||||
eventTime,
|
||||
firstTimestamp,
|
||||
involvedObject,
|
||||
lastTimestamp,
|
||||
message,
|
||||
reason,
|
||||
related,
|
||||
reportingComponent,
|
||||
reportingInstance,
|
||||
series,
|
||||
source,
|
||||
type,
|
||||
...rest
|
||||
}: KubeEventData) {
|
||||
super(rest);
|
||||
this.action = action;
|
||||
this.count = count;
|
||||
this.eventTime = eventTime;
|
||||
this.firstTimestamp = firstTimestamp;
|
||||
this.involvedObject = involvedObject;
|
||||
this.lastTimestamp = lastTimestamp;
|
||||
this.message = message;
|
||||
this.reason = reason;
|
||||
this.related = related;
|
||||
this.reportingComponent = reportingComponent;
|
||||
this.reportingInstance = reportingInstance;
|
||||
this.series = series;
|
||||
this.source = source;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
isWarning() {
|
||||
return this.type === "Warning";
|
||||
}
|
||||
|
||||
getSource() {
|
||||
if (!this.source?.component) {
|
||||
return "<unknown>";
|
||||
}
|
||||
|
||||
const { component, host = "" } = this.source;
|
||||
|
||||
return `${component} ${host}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This function is not reactive to changing of time. If rendering use `<ReactiveDuration />` instead
|
||||
*/
|
||||
getFirstSeenTime() {
|
||||
const diff = moment().diff(this.firstTimestamp);
|
||||
|
||||
return formatDuration(diff, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This function is not reactive to changing of time. If rendering use `<ReactiveDuration />` instead
|
||||
*/
|
||||
getLastSeenTime() {
|
||||
const diff = moment().diff(this.lastTimestamp);
|
||||
|
||||
return formatDuration(diff, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { OptionVariant } from "@k8slens/utilities";
|
||||
import type { LabelSelector, BaseKubeObjectCondition, NamespaceScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import type { CrossVersionObjectReference } from "../types/cross-version-object-reference";
|
||||
|
||||
export type HpaMetricType = "Resource" | "Pods" | "Object" | "External" | "ContainerResource";
|
||||
|
||||
export interface MetricCurrentTarget {
|
||||
current?: string;
|
||||
target?: string;
|
||||
}
|
||||
|
||||
export interface HorizontalPodAutoscalerMetricTarget {
|
||||
kind: string;
|
||||
name: string;
|
||||
apiVersion: string;
|
||||
}
|
||||
|
||||
export interface V2ContainerResourceMetricSource {
|
||||
container: string;
|
||||
name: string;
|
||||
target?: {
|
||||
averageUtilization?: number;
|
||||
averageValue?: string;
|
||||
type?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface V2Beta1ContainerResourceMetricSource {
|
||||
container: string;
|
||||
name: string;
|
||||
targetAverageUtilization?: number;
|
||||
targetAverageValue?: string;
|
||||
}
|
||||
|
||||
export type ContainerResourceMetricSource = V2ContainerResourceMetricSource | V2Beta1ContainerResourceMetricSource;
|
||||
|
||||
export interface V2ExternalMetricSource {
|
||||
metricName?: string;
|
||||
metricSelector?: LabelSelector;
|
||||
metric?: {
|
||||
name?: string;
|
||||
selector?: LabelSelector;
|
||||
};
|
||||
target?: {
|
||||
type: string;
|
||||
value?: string;
|
||||
averageValue?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface V2Beta1ExternalMetricSource {
|
||||
metricName?: string;
|
||||
metricSelector?: LabelSelector;
|
||||
targetAverageValue?: string;
|
||||
targetValue?: string;
|
||||
metric?: {
|
||||
selector?: LabelSelector;
|
||||
};
|
||||
}
|
||||
|
||||
export type ExternalMetricSource = V2Beta1ExternalMetricSource | V2ExternalMetricSource;
|
||||
|
||||
export interface V2ObjectMetricSource {
|
||||
metric?: {
|
||||
name?: string;
|
||||
selector?: LabelSelector;
|
||||
};
|
||||
target?: {
|
||||
type?: string;
|
||||
value?: string;
|
||||
averageValue?: string;
|
||||
};
|
||||
describedObject?: CrossVersionObjectReference;
|
||||
}
|
||||
|
||||
export interface V2Beta1ObjectMetricSource {
|
||||
averageValue?: string;
|
||||
metricName?: string;
|
||||
selector?: LabelSelector;
|
||||
targetValue?: string;
|
||||
describedObject?: CrossVersionObjectReference;
|
||||
}
|
||||
|
||||
export type ObjectMetricSource = V2ObjectMetricSource | V2Beta1ObjectMetricSource;
|
||||
|
||||
export interface V2PodsMetricSource {
|
||||
metric?: {
|
||||
name?: string;
|
||||
selector?: LabelSelector;
|
||||
};
|
||||
target?: {
|
||||
averageValue?: string;
|
||||
type?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface V2Beta1PodsMetricSource {
|
||||
metricName?: string;
|
||||
selector?: LabelSelector;
|
||||
targetAverageValue?: string;
|
||||
}
|
||||
|
||||
export type PodsMetricSource = V2PodsMetricSource | V2Beta1PodsMetricSource;
|
||||
|
||||
export interface V2ResourceMetricSource {
|
||||
name: string;
|
||||
target?: {
|
||||
averageUtilization?: number;
|
||||
averageValue?: string;
|
||||
type?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface V2Beta1ResourceMetricSource {
|
||||
name: string;
|
||||
targetAverageUtilization?: number;
|
||||
targetAverageValue?: string;
|
||||
}
|
||||
|
||||
export type ResourceMetricSource = V2ResourceMetricSource | V2Beta1ResourceMetricSource;
|
||||
|
||||
export interface BaseHorizontalPodAutoscalerMetricSpec {
|
||||
containerResource: ContainerResourceMetricSource;
|
||||
external: ExternalMetricSource;
|
||||
object: ObjectMetricSource;
|
||||
pods: PodsMetricSource;
|
||||
resource: ResourceMetricSource;
|
||||
}
|
||||
|
||||
export type HorizontalPodAutoscalerMetricSpec =
|
||||
| OptionVariant<"Resource", BaseHorizontalPodAutoscalerMetricSpec, "resource">
|
||||
| OptionVariant<"External", BaseHorizontalPodAutoscalerMetricSpec, "external">
|
||||
| OptionVariant<"Object", BaseHorizontalPodAutoscalerMetricSpec, "object">
|
||||
| OptionVariant<"Pods", BaseHorizontalPodAutoscalerMetricSpec, "pods">
|
||||
| OptionVariant<"ContainerResource", BaseHorizontalPodAutoscalerMetricSpec, "containerResource">;
|
||||
|
||||
interface HorizontalPodAutoscalerBehavior {
|
||||
scaleUp?: HPAScalingRules;
|
||||
scaleDown?: HPAScalingRules;
|
||||
}
|
||||
|
||||
interface HPAScalingRules {
|
||||
stabilizationWindowSecond?: number;
|
||||
selectPolicy?: ScalingPolicySelect;
|
||||
policies?: HPAScalingPolicy[];
|
||||
}
|
||||
|
||||
type ScalingPolicySelect = string;
|
||||
|
||||
interface HPAScalingPolicy {
|
||||
type: HPAScalingPolicyType;
|
||||
value: number;
|
||||
periodSeconds: number;
|
||||
}
|
||||
|
||||
type HPAScalingPolicyType = string;
|
||||
|
||||
export interface V2ContainerResourceMetricStatus {
|
||||
container?: string;
|
||||
name: string;
|
||||
current?: {
|
||||
averageUtilization?: number;
|
||||
averageValue?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface V2Beta1ContainerResourceMetricStatus {
|
||||
container?: string;
|
||||
currentAverageUtilization?: number;
|
||||
currentAverageValue?: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export type ContainerResourceMetricStatus = V2ContainerResourceMetricStatus | V2Beta1ContainerResourceMetricStatus;
|
||||
|
||||
export interface V2ExternalMetricStatus {
|
||||
metric?: {
|
||||
name?: string;
|
||||
selector?: LabelSelector;
|
||||
};
|
||||
current?: {
|
||||
averageValue?: string;
|
||||
value?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface V2Beta1ExternalMetricStatus {
|
||||
currentAverageValue?: string;
|
||||
currentValue?: string;
|
||||
metricName?: string;
|
||||
metricSelector?: LabelSelector;
|
||||
}
|
||||
|
||||
export type ExternalMetricStatus = V2Beta1ExternalMetricStatus | V2ExternalMetricStatus;
|
||||
|
||||
export interface V2ObjectMetricStatus {
|
||||
metric?: {
|
||||
name?: string;
|
||||
selector?: LabelSelector;
|
||||
};
|
||||
current?: {
|
||||
type?: string;
|
||||
value?: string;
|
||||
averageValue?: string;
|
||||
};
|
||||
describedObject?: CrossVersionObjectReference;
|
||||
}
|
||||
|
||||
export interface V2Beta1ObjectMetricStatus {
|
||||
averageValue?: string;
|
||||
currentValue?: string;
|
||||
metricName?: string;
|
||||
selector?: LabelSelector;
|
||||
describedObject?: CrossVersionObjectReference;
|
||||
}
|
||||
|
||||
export type ObjectMetricStatus = V2Beta1ObjectMetricStatus | V2ObjectMetricStatus;
|
||||
|
||||
export interface V2PodsMetricStatus {
|
||||
selector?: LabelSelector;
|
||||
metric?: {
|
||||
name?: string;
|
||||
selector?: LabelSelector;
|
||||
};
|
||||
current?: {
|
||||
averageValue?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface V2Beta1PodsMetricStatus {
|
||||
currentAverageValue?: string;
|
||||
metricName?: string;
|
||||
selector?: LabelSelector;
|
||||
}
|
||||
|
||||
export type PodsMetricStatus = V2Beta1PodsMetricStatus | V2PodsMetricStatus;
|
||||
|
||||
export interface V2ResourceMetricStatus {
|
||||
name: string;
|
||||
current?: {
|
||||
averageUtilization?: number;
|
||||
averageValue?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface V2Beta1ResourceMetricStatus {
|
||||
currentAverageUtilization?: number;
|
||||
currentAverageValue?: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export type ResourceMetricStatus = V2Beta1ResourceMetricStatus | V2ResourceMetricStatus;
|
||||
|
||||
export interface BaseHorizontalPodAutoscalerMetricStatus {
|
||||
containerResource: ContainerResourceMetricStatus;
|
||||
external: ExternalMetricStatus;
|
||||
object: ObjectMetricStatus;
|
||||
pods: PodsMetricStatus;
|
||||
resource: ResourceMetricStatus;
|
||||
}
|
||||
|
||||
export type HorizontalPodAutoscalerMetricStatus =
|
||||
| OptionVariant<"Resource", BaseHorizontalPodAutoscalerMetricStatus, "resource">
|
||||
| OptionVariant<"External", BaseHorizontalPodAutoscalerMetricStatus, "external">
|
||||
| OptionVariant<"Object", BaseHorizontalPodAutoscalerMetricStatus, "object">
|
||||
| OptionVariant<"Pods", BaseHorizontalPodAutoscalerMetricStatus, "pods">
|
||||
| OptionVariant<"ContainerResource", BaseHorizontalPodAutoscalerMetricStatus, "containerResource">;
|
||||
|
||||
export interface HorizontalPodAutoscalerSpec {
|
||||
scaleTargetRef: CrossVersionObjectReference;
|
||||
minReplicas?: number;
|
||||
maxReplicas: number;
|
||||
metrics?: HorizontalPodAutoscalerMetricSpec[];
|
||||
behavior?: HorizontalPodAutoscalerBehavior;
|
||||
targetCPUUtilizationPercentage?: number; // used only in autoscaling/v1
|
||||
}
|
||||
|
||||
export interface HorizontalPodAutoscalerStatus {
|
||||
conditions?: BaseKubeObjectCondition[];
|
||||
currentReplicas: number;
|
||||
desiredReplicas: number;
|
||||
currentMetrics?: HorizontalPodAutoscalerMetricStatus[];
|
||||
currentCPUUtilizationPercentage?: number; // used only in autoscaling/v1
|
||||
}
|
||||
|
||||
export class HorizontalPodAutoscaler extends KubeObject<
|
||||
NamespaceScopedMetadata,
|
||||
HorizontalPodAutoscalerStatus,
|
||||
HorizontalPodAutoscalerSpec
|
||||
> {
|
||||
static readonly kind = "HorizontalPodAutoscaler";
|
||||
|
||||
static readonly namespaced = true;
|
||||
|
||||
static readonly apiBase = "/apis/autoscaling/v2/horizontalpodautoscalers";
|
||||
|
||||
getMaxPods() {
|
||||
return this.spec.maxReplicas ?? 0;
|
||||
}
|
||||
|
||||
getMinPods() {
|
||||
return this.spec.minReplicas ?? 0;
|
||||
}
|
||||
|
||||
getReplicas() {
|
||||
return this.status?.currentReplicas ?? 0;
|
||||
}
|
||||
|
||||
getReadyConditions() {
|
||||
return this.getConditions().filter(({ isReady }) => isReady);
|
||||
}
|
||||
|
||||
getConditions() {
|
||||
return (
|
||||
this.status?.conditions?.map((condition) => {
|
||||
const { message, reason, lastTransitionTime, status } = condition;
|
||||
|
||||
return {
|
||||
...condition,
|
||||
isReady: status === "True",
|
||||
tooltip: `${message || reason} (${lastTransitionTime})`,
|
||||
};
|
||||
}) ?? []
|
||||
);
|
||||
}
|
||||
|
||||
getMetrics() {
|
||||
return this.spec.metrics ?? [];
|
||||
}
|
||||
|
||||
getCurrentMetrics() {
|
||||
return this.status?.currentMetrics ?? [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
export * from "./cluster-role-binding";
|
||||
export * from "./cluster-role";
|
||||
export * from "./cluster";
|
||||
export * from "./component-status";
|
||||
export * from "./config-map";
|
||||
export * from "./cron-job";
|
||||
export * from "./custom-resource-definition";
|
||||
export * from "./daemon-set";
|
||||
export * from "./deployment";
|
||||
export * from "./endpoint";
|
||||
export * from "./events";
|
||||
export * from "./horizontal-pod-autoscaler";
|
||||
export * from "./ingress-class";
|
||||
export * from "./ingress";
|
||||
export * from "./job";
|
||||
export * from "./lease";
|
||||
export * from "./limit-range";
|
||||
export * from "./mutating-webhook-configuration";
|
||||
export * from "./namespace";
|
||||
export * from "./network-policy";
|
||||
export * from "./node";
|
||||
export * from "./persistent-volume-claim";
|
||||
export * from "./persistent-volume";
|
||||
export * from "./pod-disruption-budget";
|
||||
export * from "./pod-metrics";
|
||||
export * from "./pod-security-policy";
|
||||
export * from "./pod";
|
||||
export * from "./priority-class";
|
||||
export * from "./replica-set";
|
||||
export * from "./replication-controller";
|
||||
export * from "./resource-quota";
|
||||
export * from "./role-binding";
|
||||
export * from "./role";
|
||||
export * from "./runtime-class";
|
||||
export * from "./secret";
|
||||
export * from "./self-subject-rules-reviews";
|
||||
export * from "./service-account";
|
||||
export * from "./service";
|
||||
export * from "./stateful-set";
|
||||
export * from "./storage-class";
|
||||
export * from "./validating-webhook-configuration";
|
||||
export * from "./vertical-pod-autoscaler";
|
||||
@@ -0,0 +1,61 @@
|
||||
import type { ClusterScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
|
||||
export interface IngressClassParametersReference {
|
||||
/**
|
||||
* For example: `"k8s.example.net"`
|
||||
*/
|
||||
apiGroup: string;
|
||||
scope: "Namespace" | "Cluster";
|
||||
kind: "ClusterIngressParameter" | "IngressParameter";
|
||||
name: string;
|
||||
/**
|
||||
* The namespace for IngressClass must be defined in `spec.parameters.namespace` instead of `metadata.namespace` (!)
|
||||
*/
|
||||
namespace?: string;
|
||||
}
|
||||
|
||||
export interface IngressClassSpec {
|
||||
controller: string; // "example.com/ingress-controller"
|
||||
parameters?: IngressClassParametersReference;
|
||||
}
|
||||
|
||||
export interface IngressClassStatus {}
|
||||
|
||||
export class IngressClass extends KubeObject<ClusterScopedMetadata, IngressClassStatus, IngressClassSpec> {
|
||||
static readonly kind = "IngressClass";
|
||||
|
||||
static readonly namespaced = false;
|
||||
|
||||
static readonly apiBase = "/apis/networking.k8s.io/v1/ingressclasses";
|
||||
|
||||
static readonly ANNOTATION_IS_DEFAULT = "ingressclass.kubernetes.io/is-default-class";
|
||||
|
||||
getController(): string {
|
||||
return this.spec.controller;
|
||||
}
|
||||
|
||||
getCtrlApiGroup() {
|
||||
return this.spec.parameters?.apiGroup;
|
||||
}
|
||||
|
||||
getCtrlScope() {
|
||||
return this.spec.parameters?.scope;
|
||||
}
|
||||
|
||||
getCtrlNs() {
|
||||
return this.spec.parameters?.namespace;
|
||||
}
|
||||
|
||||
getCtrlKind() {
|
||||
return this.spec.parameters?.kind;
|
||||
}
|
||||
|
||||
getCtrlName() {
|
||||
return this.spec.parameters?.name;
|
||||
}
|
||||
|
||||
get isDefault() {
|
||||
return this.metadata.annotations?.[IngressClass.ANNOTATION_IS_DEFAULT] === "true";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { hasTypedProperty, isString, iter } from "@k8slens/utilities";
|
||||
import type { RequireExactlyOne } from "type-fest";
|
||||
import type { TypedLocalObjectReference, NamespaceScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
|
||||
export interface ILoadBalancerIngress {
|
||||
hostname?: string;
|
||||
ip?: string;
|
||||
}
|
||||
|
||||
// extensions/v1beta1
|
||||
export interface ExtensionsBackend {
|
||||
serviceName?: string;
|
||||
servicePort?: number | string;
|
||||
}
|
||||
|
||||
// networking.k8s.io/v1
|
||||
export interface NetworkingBackend {
|
||||
service?: IngressService;
|
||||
}
|
||||
|
||||
export type IngressBackend = (ExtensionsBackend | NetworkingBackend) & {
|
||||
resource?: TypedLocalObjectReference;
|
||||
};
|
||||
|
||||
export interface IngressService {
|
||||
name: string;
|
||||
port: RequireExactlyOne<{
|
||||
name: string;
|
||||
number: number;
|
||||
}>;
|
||||
}
|
||||
|
||||
function isExtensionsBackend(backend: IngressBackend): backend is ExtensionsBackend {
|
||||
return hasTypedProperty(backend, "serviceName", isString);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line xss/no-mixed-html
|
||||
const unknownPortString = "<unknown>";
|
||||
|
||||
/**
|
||||
* Format an ingress backend into the name of the service and port
|
||||
* @param backend The ingress target
|
||||
*/
|
||||
export function getBackendServiceNamePort(backend: IngressBackend | undefined): string {
|
||||
if (!backend) {
|
||||
return unknownPortString;
|
||||
}
|
||||
|
||||
if (isExtensionsBackend(backend)) {
|
||||
return `${backend.serviceName}:${backend.servicePort}`;
|
||||
}
|
||||
|
||||
if (backend.service) {
|
||||
const { name, port } = backend.service;
|
||||
|
||||
return `${name}:${port.number ?? port.name}`;
|
||||
}
|
||||
|
||||
return unknownPortString;
|
||||
}
|
||||
|
||||
export interface HTTPIngressPath {
|
||||
pathType: "Exact" | "Prefix" | "ImplementationSpecific";
|
||||
path?: string;
|
||||
backend?: IngressBackend;
|
||||
}
|
||||
|
||||
export interface HTTPIngressRuleValue {
|
||||
paths: HTTPIngressPath[];
|
||||
}
|
||||
|
||||
export interface IngressRule {
|
||||
host?: string;
|
||||
http?: HTTPIngressRuleValue;
|
||||
}
|
||||
|
||||
export interface IngressSpec {
|
||||
tls: {
|
||||
secretName: string;
|
||||
}[];
|
||||
rules?: IngressRule[];
|
||||
// extensions/v1beta1
|
||||
backend?: ExtensionsBackend;
|
||||
/**
|
||||
* The default backend which is exactly on of:
|
||||
* - service
|
||||
* - resource
|
||||
*/
|
||||
defaultBackend?: RequireExactlyOne<
|
||||
NetworkingBackend & {
|
||||
resource: {
|
||||
apiGroup: string;
|
||||
kind: string;
|
||||
name: string;
|
||||
};
|
||||
}
|
||||
>;
|
||||
}
|
||||
|
||||
export interface IngressStatus {
|
||||
loadBalancer: {
|
||||
ingress?: ILoadBalancerIngress[];
|
||||
};
|
||||
}
|
||||
|
||||
export class Ingress extends KubeObject<NamespaceScopedMetadata, IngressStatus, IngressSpec> {
|
||||
static readonly kind = "Ingress";
|
||||
|
||||
static readonly namespaced = true;
|
||||
|
||||
static readonly apiBase = "/apis/networking.k8s.io/v1/ingresses";
|
||||
|
||||
getRules() {
|
||||
return this.spec.rules ?? [];
|
||||
}
|
||||
|
||||
getRoutes(): string[] {
|
||||
return computeRouteDeclarations(this).map(({ url, service }) => `${url} ⇢ ${service}`);
|
||||
}
|
||||
|
||||
getServiceNamePort(): ExtensionsBackend | undefined {
|
||||
const { spec: { backend, defaultBackend } = {} } = this;
|
||||
|
||||
const serviceName = defaultBackend?.service?.name ?? backend?.serviceName;
|
||||
const servicePort =
|
||||
defaultBackend?.service?.port.number ?? defaultBackend?.service?.port.name ?? backend?.servicePort;
|
||||
|
||||
if (!serviceName || !servicePort) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
serviceName,
|
||||
servicePort,
|
||||
};
|
||||
}
|
||||
|
||||
getHosts() {
|
||||
const {
|
||||
spec: { rules = [] },
|
||||
} = this;
|
||||
|
||||
return [...iter.filterMap(rules, (rule) => rule.host)];
|
||||
}
|
||||
|
||||
getPorts() {
|
||||
const ports: number[] = [];
|
||||
const {
|
||||
spec: { tls, rules = [], backend, defaultBackend },
|
||||
} = this;
|
||||
const httpPort = 80;
|
||||
const tlsPort = 443;
|
||||
// Note: not using the port name (string)
|
||||
const servicePort = defaultBackend?.service?.port.number ?? backend?.servicePort;
|
||||
|
||||
if (rules.length > 0) {
|
||||
if (rules.some((rule) => rule.http)) {
|
||||
ports.push(httpPort);
|
||||
}
|
||||
} else if (servicePort !== undefined) {
|
||||
ports.push(Number(servicePort));
|
||||
}
|
||||
|
||||
if (tls && tls.length > 0) {
|
||||
ports.push(tlsPort);
|
||||
}
|
||||
|
||||
return ports.join(", ");
|
||||
}
|
||||
|
||||
getLoadBalancers() {
|
||||
return this.status?.loadBalancer?.ingress?.map((address) => address.hostname || address.ip) ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
export interface ComputedIngressRoute {
|
||||
displayAsLink: boolean;
|
||||
pathname: string;
|
||||
url: string;
|
||||
service: string;
|
||||
}
|
||||
|
||||
export function computeRuleDeclarations(ingress: Ingress, rule: IngressRule): ComputedIngressRoute[] {
|
||||
const { host = "*", http: { paths } = { paths: [] } } = rule;
|
||||
const protocol = (ingress.spec?.tls?.length ?? 0) === 0 ? "http" : "https";
|
||||
|
||||
return paths.map(({ path = "/", backend }) => ({
|
||||
displayAsLink: !host.includes("*"),
|
||||
pathname: path,
|
||||
url: `${protocol}://${host}${path}`,
|
||||
service: getBackendServiceNamePort(backend),
|
||||
}));
|
||||
}
|
||||
|
||||
export function computeRouteDeclarations(ingress: Ingress): ComputedIngressRoute[] {
|
||||
return ingress.getRules().flatMap((rule) => computeRuleDeclarations(ingress, rule));
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { LabelSelector, KubeObjectStatus, NamespaceScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import type { Container } from "../types/container";
|
||||
import type { PodSpec } from "./pod";
|
||||
|
||||
export interface JobSpec {
|
||||
parallelism?: number;
|
||||
completions?: number;
|
||||
backoffLimit?: number;
|
||||
selector?: LabelSelector;
|
||||
template: {
|
||||
metadata: {
|
||||
creationTimestamp?: string;
|
||||
labels?: Partial<Record<string, string>>;
|
||||
annotations?: Partial<Record<string, string>>;
|
||||
};
|
||||
spec: PodSpec;
|
||||
};
|
||||
containers?: Container[];
|
||||
restartPolicy?: string;
|
||||
terminationGracePeriodSeconds?: number;
|
||||
dnsPolicy?: string;
|
||||
serviceAccountName?: string;
|
||||
serviceAccount?: string;
|
||||
schedulerName?: string;
|
||||
}
|
||||
|
||||
export interface JobStatus extends KubeObjectStatus {
|
||||
startTime: string;
|
||||
completionTime: string;
|
||||
succeeded: number;
|
||||
}
|
||||
|
||||
export class Job extends KubeObject<NamespaceScopedMetadata, JobStatus, JobSpec> {
|
||||
static readonly kind = "Job";
|
||||
|
||||
static readonly namespaced = true;
|
||||
|
||||
static readonly apiBase = "/apis/batch/v1/jobs";
|
||||
|
||||
getSelectors(): string[] {
|
||||
return KubeObject.stringifyLabels(this.spec.selector?.matchLabels);
|
||||
}
|
||||
|
||||
getNodeSelectors(): string[] {
|
||||
return KubeObject.stringifyLabels(this.spec.template.spec.nodeSelector);
|
||||
}
|
||||
|
||||
getTemplateLabels(): string[] {
|
||||
return KubeObject.stringifyLabels(this.spec.template.metadata.labels);
|
||||
}
|
||||
|
||||
getTolerations() {
|
||||
return this.spec.template.spec.tolerations ?? [];
|
||||
}
|
||||
|
||||
getAffinity() {
|
||||
return this.spec.template.spec.affinity;
|
||||
}
|
||||
|
||||
getAffinityNumber() {
|
||||
return Object.keys(this.getAffinity() ?? {}).length;
|
||||
}
|
||||
|
||||
getDesiredCompletions() {
|
||||
return this.spec.completions ?? 0;
|
||||
}
|
||||
|
||||
getCompletions() {
|
||||
return this.status?.succeeded ?? 0;
|
||||
}
|
||||
|
||||
getParallelism() {
|
||||
return this.spec.parallelism;
|
||||
}
|
||||
|
||||
getCondition() {
|
||||
// Type of Job condition could be only Complete or Failed
|
||||
// https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.21/#jobcondition-v1-batch
|
||||
return this.status?.conditions?.find(({ status }) => status === "True");
|
||||
}
|
||||
|
||||
getImages() {
|
||||
return this.spec.template.spec.containers?.map((container) => container.image) ?? [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { NamespaceScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
|
||||
export interface LeaseSpec {
|
||||
acquireTime?: string;
|
||||
holderIdentity: string;
|
||||
leaseDurationSeconds: number;
|
||||
leaseTransitions?: number;
|
||||
renewTime: string;
|
||||
}
|
||||
|
||||
export class Lease extends KubeObject<NamespaceScopedMetadata, void, LeaseSpec> {
|
||||
static readonly kind = "Lease";
|
||||
|
||||
static readonly namespaced = true;
|
||||
|
||||
static readonly apiBase = "/apis/coordination.k8s.io/v1/leases";
|
||||
|
||||
getAcquireTime(): string {
|
||||
return this.spec.acquireTime || "";
|
||||
}
|
||||
|
||||
getHolderIdentity(): string {
|
||||
return this.spec.holderIdentity;
|
||||
}
|
||||
|
||||
getLeaseDurationSeconds(): number {
|
||||
return this.spec.leaseDurationSeconds;
|
||||
}
|
||||
|
||||
getLeaseTransitions(): number | undefined {
|
||||
return this.spec.leaseTransitions;
|
||||
}
|
||||
|
||||
getRenewTime(): string {
|
||||
return this.spec.renewTime;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import type { NamespaceScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
export enum LimitType {
|
||||
CONTAINER = "Container",
|
||||
POD = "Pod",
|
||||
PVC = "PersistentVolumeClaim",
|
||||
}
|
||||
|
||||
export enum Resource {
|
||||
MEMORY = "memory",
|
||||
CPU = "cpu",
|
||||
STORAGE = "storage",
|
||||
EPHEMERAL_STORAGE = "ephemeral-storage",
|
||||
}
|
||||
|
||||
export enum LimitPart {
|
||||
MAX = "max",
|
||||
MIN = "min",
|
||||
DEFAULT = "default",
|
||||
DEFAULT_REQUEST = "defaultRequest",
|
||||
MAX_LIMIT_REQUEST_RATIO = "maxLimitRequestRatio",
|
||||
}
|
||||
|
||||
type LimitRangeParts = Partial<Record<LimitPart, Record<string, string>>>;
|
||||
|
||||
export interface LimitRangeItem extends LimitRangeParts {
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface LimitRangeSpec {
|
||||
limits: LimitRangeItem[];
|
||||
}
|
||||
|
||||
export class LimitRange extends KubeObject<NamespaceScopedMetadata, void, LimitRangeSpec> {
|
||||
static readonly kind = "LimitRange";
|
||||
|
||||
static readonly namespaced = true;
|
||||
|
||||
static readonly apiBase = "/api/v1/limitranges";
|
||||
|
||||
getContainerLimits() {
|
||||
return this.spec.limits.filter((limit) => limit.type === LimitType.CONTAINER);
|
||||
}
|
||||
|
||||
getPodLimits() {
|
||||
return this.spec.limits.filter((limit) => limit.type === LimitType.POD);
|
||||
}
|
||||
|
||||
getPVCLimits() {
|
||||
return this.spec.limits.filter((limit) => limit.type === LimitType.PVC);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type {
|
||||
LabelSelector,
|
||||
KubeJsonApiData,
|
||||
KubeObjectMetadata,
|
||||
KubeObjectScope,
|
||||
NamespaceScopedMetadata,
|
||||
} from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
|
||||
export interface WebhookClientConfig {
|
||||
/**
|
||||
* The location of the webhook
|
||||
*/
|
||||
url?: string;
|
||||
|
||||
/**
|
||||
* A reference to the service for this webhook. Either `service` or `url` must be specified.
|
||||
*/
|
||||
service?: ServiceReference;
|
||||
|
||||
/**
|
||||
* a PEM encoded CA bundle which will be used to validate the webhook's server certificate.
|
||||
* If unspecified, system trust roots on the apiserver are used.
|
||||
*/
|
||||
caBundle?: string;
|
||||
}
|
||||
|
||||
export interface RuleWithOperations {
|
||||
/**
|
||||
* The API groups the resources belong to. '*' is all groups.
|
||||
* If '*' is present, the length of the slice must be one.
|
||||
*/
|
||||
apiGroups: string[];
|
||||
|
||||
/**
|
||||
* The API versions the resources belong to. '*' is all versions.
|
||||
*/
|
||||
apiVersions?: string[];
|
||||
|
||||
/**
|
||||
* A list of resources this rule applies to.
|
||||
* For example:
|
||||
* - 'pods' means pods.
|
||||
* - '*' means all resources, but not subresources.
|
||||
* - 'pods/' means all subresources of pods.
|
||||
* - '*\/scale' means all scale subresources. Allowed values are "Resource" / "Resource/Scale" / "Resource/Status".
|
||||
*/
|
||||
resources: string[];
|
||||
|
||||
/**
|
||||
* A list of operations this rule applies to.
|
||||
*/
|
||||
operations: ("CREATE" | "UPDATE" | "DELETE" | "CONNECT")[];
|
||||
|
||||
/**
|
||||
* The scope of this rule.
|
||||
*
|
||||
* @default "Cluster"
|
||||
*/
|
||||
scope?: "Cluster" | "Namespace";
|
||||
}
|
||||
|
||||
export interface Webhook {
|
||||
/**
|
||||
* The name of the webhook configuration.
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* Defines how to communicate with the hook.
|
||||
*/
|
||||
clientConfig: WebhookClientConfig;
|
||||
|
||||
/**
|
||||
* Rules describes what operations on what resources/subresources the webhook cares about.
|
||||
* The webhook cares about an operation if it matches _any_ Rule.
|
||||
*/
|
||||
rules?: RuleWithOperations[];
|
||||
|
||||
/**
|
||||
* An ordered list of preferred `AdmissionReview` versions the webhook expects.
|
||||
* API server will try to use first version in the list which it supports.
|
||||
* If none of the versions specified in this list supported by API server, validation will fail for this object.
|
||||
*/
|
||||
admissionReviewVersions?: string[];
|
||||
|
||||
/**
|
||||
* The timeout for this webhook.
|
||||
* After the timeout passes, the webhook call will be ignored
|
||||
* or the API call will fail depending on the failure policy.
|
||||
*/
|
||||
timeoutSeconds?: number;
|
||||
|
||||
/**
|
||||
* Specifies how unrecognized errors from the webhook are handled
|
||||
* @default "Fail"
|
||||
*/
|
||||
failurePolicy?: "Ignore" | "Fail";
|
||||
|
||||
/**
|
||||
* Defines how the "rules" list is used to match incoming requests.
|
||||
* - Exact: match a request only if it exactly matches a specified rule.
|
||||
* - Equivalent: match a request if modifies a resource listed in rules, even via another API group or version.
|
||||
* @default "Equivalent"
|
||||
*/
|
||||
matchPolicy?: "Exact" | "Equivalent";
|
||||
|
||||
// NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object
|
||||
// matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels.
|
||||
// If both the object and the webhook configuration specify namespaceSelector, they must match.
|
||||
namespaceSelector?: LabelSelector;
|
||||
|
||||
// ObjectSelector decides whether to run the webhook based on if the object has matching labels.
|
||||
// objectSelector and namespaceSelector are ANDed. An empty objectSelector matches all objects.
|
||||
// A null objectSelector matches no objects.
|
||||
objectSelector?: LabelSelector;
|
||||
|
||||
// SideEffects states whether this webhookk should run when no mutating or validating webhook
|
||||
// needs to run. This should be false when the webhook only applies to resources that have
|
||||
// the sideEffects field set to None. Defaults to true.
|
||||
sideEffects?: string;
|
||||
|
||||
/**
|
||||
* Indicates whether this webhook should be called multiple times as part of a single admission evaluation.
|
||||
*/
|
||||
reinvocationPolicy?: "Never" | "IfNeeded";
|
||||
}
|
||||
|
||||
export interface ServiceReference {
|
||||
/**
|
||||
* The namespace of the service.
|
||||
*/
|
||||
namespace: string;
|
||||
|
||||
/**
|
||||
* The name of the service.
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* The URL path which will be sent in any request to this service.
|
||||
*/
|
||||
path?: string;
|
||||
|
||||
/**
|
||||
* The service port which will be used when accessing the service.
|
||||
*/
|
||||
port?: number | string;
|
||||
}
|
||||
|
||||
export interface MutatingWebhookConfigurationData
|
||||
extends KubeJsonApiData<KubeObjectMetadata<KubeObjectScope.Namespace>, void, void> {
|
||||
webhooks?: Webhook[];
|
||||
}
|
||||
|
||||
export class MutatingWebhookConfiguration extends KubeObject<NamespaceScopedMetadata, void, void> {
|
||||
static kind = "MutatingWebhookConfiguration";
|
||||
|
||||
static namespaced = false;
|
||||
|
||||
static apiBase = "/apis/admissionregistration.k8s.io/v1/mutatingwebhookconfigurations";
|
||||
|
||||
webhooks?: Webhook[];
|
||||
|
||||
constructor({ webhooks, ...rest }: MutatingWebhookConfigurationData) {
|
||||
super(rest);
|
||||
this.webhooks = webhooks;
|
||||
}
|
||||
|
||||
getWebhooks(): Webhook[] {
|
||||
return this.webhooks ?? [];
|
||||
}
|
||||
|
||||
getClientConfig(serviceName: string, serviceNamespace: string): WebhookClientConfig | undefined {
|
||||
const webhooks = this.getWebhooks();
|
||||
|
||||
for (const webhook of webhooks) {
|
||||
if (
|
||||
webhook.clientConfig.service?.name === serviceName &&
|
||||
webhook.clientConfig.service?.namespace === serviceNamespace
|
||||
) {
|
||||
return webhook.clientConfig;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { KubeObjectStatus, ClusterScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
|
||||
export enum NamespaceStatusKind {
|
||||
ACTIVE = "Active",
|
||||
TERMINATING = "Terminating",
|
||||
}
|
||||
|
||||
export interface NamespaceSpec {
|
||||
finalizers?: string[];
|
||||
}
|
||||
|
||||
export interface NamespaceStatus extends KubeObjectStatus {
|
||||
phase?: string;
|
||||
}
|
||||
|
||||
export class Namespace extends KubeObject<ClusterScopedMetadata, NamespaceStatus, NamespaceSpec> {
|
||||
static readonly kind = "Namespace";
|
||||
|
||||
static readonly namespaced = false;
|
||||
|
||||
static readonly apiBase = "/api/v1/namespaces";
|
||||
|
||||
getStatus() {
|
||||
return this.status?.phase ?? "-";
|
||||
}
|
||||
|
||||
isSubnamespace() {
|
||||
return this.getAnnotations().find((annotation) => annotation.includes("hnc.x-k8s.io/subnamespace-of"));
|
||||
}
|
||||
|
||||
isChildOf(parentName: string) {
|
||||
return this.getLabels().find((label) => label === `${parentName}.tree.hnc.x-k8s.io/depth=1`);
|
||||
}
|
||||
|
||||
isControlledByHNC() {
|
||||
return this.getLabels().includes("hnc.x-k8s.io/included-namespace=true");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { LabelSelector, NamespaceScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
|
||||
export interface PolicyIpBlock {
|
||||
cidr: string;
|
||||
except?: string[];
|
||||
}
|
||||
|
||||
export interface NetworkPolicyPort {
|
||||
/**
|
||||
* The protocol which network traffic must match.
|
||||
*
|
||||
* One of:
|
||||
* - `"TCP"`
|
||||
* - `"UDP"`
|
||||
* - `"SCTP"`
|
||||
*
|
||||
* @default "TCP"
|
||||
*/
|
||||
protocol?: string;
|
||||
|
||||
/**
|
||||
* The port on the given protocol. This can either be a numerical or named
|
||||
* port on a pod. If this field is not provided, this matches all port names and
|
||||
* numbers.
|
||||
*
|
||||
* If present, only traffic on the specified protocol AND port will be matched.
|
||||
*/
|
||||
port?: number | string;
|
||||
|
||||
/**
|
||||
* If set, indicates that the range of ports from port to endPort, inclusive,
|
||||
* should be allowed by the policy. This field cannot be defined if the port field
|
||||
* is not defined or if the port field is defined as a named (string) port.
|
||||
*
|
||||
* The endPort must be equal or greater than port.
|
||||
*/
|
||||
endPort?: number;
|
||||
}
|
||||
|
||||
export interface NetworkPolicyPeer {
|
||||
/**
|
||||
* IPBlock defines policy on a particular IPBlock. If this field is set then
|
||||
* neither of the other fields can be.
|
||||
*/
|
||||
ipBlock?: PolicyIpBlock;
|
||||
|
||||
/**
|
||||
* Selects Namespaces using cluster-scoped labels. This field follows standard label
|
||||
* selector semantics; if present but empty, it selects all namespaces.
|
||||
*
|
||||
* If PodSelector is also set, then the NetworkPolicyPeer as a whole selects
|
||||
* the Pods matching PodSelector in the Namespaces selected by NamespaceSelector.
|
||||
*
|
||||
* Otherwise it selects all Pods in the Namespaces selected by NamespaceSelector.
|
||||
*/
|
||||
namespaceSelector?: LabelSelector;
|
||||
|
||||
/**
|
||||
* This is a label selector which selects Pods. This field follows standard label
|
||||
* selector semantics; if present but empty, it selects all pods.
|
||||
*
|
||||
* If NamespaceSelector is also set, then the NetworkPolicyPeer as a whole selects
|
||||
* the Pods matching PodSelector in the Namespaces selected by NamespaceSelector.
|
||||
*
|
||||
* Otherwise it selects the Pods matching PodSelector in the policy's own Namespace.
|
||||
*/
|
||||
podSelector?: LabelSelector;
|
||||
}
|
||||
|
||||
export interface PolicyIngress {
|
||||
from?: NetworkPolicyPeer[];
|
||||
ports?: NetworkPolicyPort[];
|
||||
}
|
||||
|
||||
export interface PolicyEgress {
|
||||
to?: NetworkPolicyPeer[];
|
||||
ports?: NetworkPolicyPort[];
|
||||
}
|
||||
|
||||
export type PolicyType = "Ingress" | "Egress";
|
||||
|
||||
export interface NetworkPolicySpec {
|
||||
podSelector: LabelSelector;
|
||||
policyTypes?: PolicyType[];
|
||||
ingress?: PolicyIngress[];
|
||||
egress?: PolicyEgress[];
|
||||
}
|
||||
|
||||
export class NetworkPolicy extends KubeObject<NamespaceScopedMetadata, void, NetworkPolicySpec> {
|
||||
static readonly kind = "NetworkPolicy";
|
||||
|
||||
static readonly namespaced = true;
|
||||
|
||||
static readonly apiBase = "/apis/networking.k8s.io/v1/networkpolicies";
|
||||
|
||||
getMatchLabels(): string[] {
|
||||
if (!this.spec.podSelector || !this.spec.podSelector.matchLabels) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Object.entries(this.spec.podSelector.matchLabels).map((data) => data.join(":"));
|
||||
}
|
||||
|
||||
getTypes(): string[] {
|
||||
if (!this.spec.policyTypes) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return this.spec.policyTypes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { Node } from "./node";
|
||||
|
||||
describe("Node tests", () => {
|
||||
describe("isMasterNode()", () => {
|
||||
it("given a master node labelled before kubernetes 1.20, should return true", () => {
|
||||
const node = new Node({
|
||||
apiVersion: "foo",
|
||||
kind: "Node",
|
||||
metadata: {
|
||||
name: "bar",
|
||||
resourceVersion: "1",
|
||||
uid: "bat",
|
||||
labels: {
|
||||
"node-role.kubernetes.io/master": "NoSchedule",
|
||||
},
|
||||
selfLink: "/api/v1/nodes/bar",
|
||||
},
|
||||
});
|
||||
|
||||
expect(node.isMasterNode()).toBe(true);
|
||||
});
|
||||
|
||||
it("given a master node labelled after kubernetes 1.20, should return true", () => {
|
||||
const node = new Node({
|
||||
apiVersion: "foo",
|
||||
kind: "Node",
|
||||
metadata: {
|
||||
name: "bar",
|
||||
resourceVersion: "1",
|
||||
uid: "bat",
|
||||
labels: {
|
||||
"node-role.kubernetes.io/control-plane": "NoSchedule",
|
||||
},
|
||||
selfLink: "/api/v1/nodes/bar",
|
||||
},
|
||||
});
|
||||
|
||||
expect(node.isMasterNode()).toBe(true);
|
||||
});
|
||||
|
||||
it("given a non master node, should return false", () => {
|
||||
const node = new Node({
|
||||
apiVersion: "foo",
|
||||
kind: "Node",
|
||||
metadata: {
|
||||
name: "bar",
|
||||
resourceVersion: "1",
|
||||
uid: "bat",
|
||||
labels: {},
|
||||
selfLink: "/api/v1/nodes/bar",
|
||||
},
|
||||
});
|
||||
|
||||
expect(node.isMasterNode()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getRoleLabels()", () => {
|
||||
it("should return empty string if labels is not present", () => {
|
||||
const node = new Node({
|
||||
apiVersion: "foo",
|
||||
kind: "Node",
|
||||
metadata: {
|
||||
name: "bar",
|
||||
resourceVersion: "1",
|
||||
uid: "bat",
|
||||
selfLink: "/api/v1/nodes/bar",
|
||||
},
|
||||
});
|
||||
|
||||
expect(node.getRoleLabels()).toBe("");
|
||||
});
|
||||
|
||||
it("should return empty string if labels is empty object", () => {
|
||||
const node = new Node({
|
||||
apiVersion: "foo",
|
||||
kind: "Node",
|
||||
metadata: {
|
||||
name: "bar",
|
||||
resourceVersion: "1",
|
||||
uid: "bat",
|
||||
labels: {},
|
||||
selfLink: "/api/v1/nodes/bar",
|
||||
},
|
||||
});
|
||||
|
||||
expect(node.getRoleLabels()).toBe("");
|
||||
});
|
||||
|
||||
it("should return rest of keys with substring node-role.kubernetes.io/", () => {
|
||||
const node = new Node({
|
||||
apiVersion: "foo",
|
||||
kind: "Node",
|
||||
metadata: {
|
||||
name: "bar",
|
||||
resourceVersion: "1",
|
||||
uid: "bat",
|
||||
labels: {
|
||||
"node-role.kubernetes.io/foobar": "bat",
|
||||
"hellonode-role.kubernetes.io/foobar1": "bat",
|
||||
},
|
||||
selfLink: "/api/v1/nodes/bar",
|
||||
},
|
||||
});
|
||||
|
||||
expect(node.getRoleLabels()).toBe("foobar, foobar1");
|
||||
});
|
||||
|
||||
it("should return rest of keys with substring node-role.kubernetes.io/ after last /", () => {
|
||||
const node = new Node({
|
||||
apiVersion: "foo",
|
||||
kind: "Node",
|
||||
metadata: {
|
||||
name: "bar",
|
||||
resourceVersion: "1",
|
||||
uid: "bat",
|
||||
labels: {
|
||||
"node-role.kubernetes.io/foobar": "bat",
|
||||
"hellonode-role.kubernetes.io//////foobar1": "bat",
|
||||
},
|
||||
selfLink: "/api/v1/nodes/bar",
|
||||
},
|
||||
});
|
||||
|
||||
expect(node.getRoleLabels()).toBe("foobar, foobar1");
|
||||
});
|
||||
|
||||
it("should return value of label kubernetes.io/role if present", () => {
|
||||
const node = new Node({
|
||||
apiVersion: "foo",
|
||||
kind: "Node",
|
||||
metadata: {
|
||||
name: "bar",
|
||||
resourceVersion: "1",
|
||||
uid: "bat",
|
||||
labels: {
|
||||
"kubernetes.io/role": "master",
|
||||
},
|
||||
selfLink: "/api/v1/nodes/bar",
|
||||
},
|
||||
});
|
||||
|
||||
expect(node.getRoleLabels()).toBe("master");
|
||||
});
|
||||
|
||||
it("should return value of label node.kubernetes.io/role if present", () => {
|
||||
const node = new Node({
|
||||
apiVersion: "foo",
|
||||
kind: "Node",
|
||||
metadata: {
|
||||
name: "bar",
|
||||
resourceVersion: "1",
|
||||
uid: "bat",
|
||||
labels: {
|
||||
"node.kubernetes.io/role": "master",
|
||||
},
|
||||
selfLink: "/api/v1/nodes/bar",
|
||||
},
|
||||
});
|
||||
|
||||
expect(node.getRoleLabels()).toBe("master");
|
||||
});
|
||||
|
||||
it("all sources should be joined together", () => {
|
||||
const node = new Node({
|
||||
apiVersion: "foo",
|
||||
kind: "Node",
|
||||
metadata: {
|
||||
name: "bar",
|
||||
resourceVersion: "1",
|
||||
uid: "bat",
|
||||
labels: {
|
||||
"aksjhdkjahsdnode-role.kubernetes.io/foobar": "bat",
|
||||
"kubernetes.io/role": "master",
|
||||
"node.kubernetes.io/role": "master-v2-max",
|
||||
},
|
||||
selfLink: "/api/v1/nodes/bar",
|
||||
},
|
||||
});
|
||||
|
||||
expect(node.getRoleLabels()).toBe("foobar, master, master-v2-max");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,253 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { isObject, cpuUnitsToNumber, unitsToBytes } from "@k8slens/utilities";
|
||||
import { TypedRegEx } from "typed-regex";
|
||||
import type { BaseKubeObjectCondition, ClusterScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
|
||||
export interface NodeTaint {
|
||||
key: string;
|
||||
value?: string;
|
||||
effect: string;
|
||||
timeAdded: string;
|
||||
}
|
||||
|
||||
export function formatNodeTaint(taint: NodeTaint): string {
|
||||
if (taint.value) {
|
||||
return `${taint.key}=${taint.value}:${taint.effect}`;
|
||||
}
|
||||
|
||||
return `${taint.key}:${taint.effect}`;
|
||||
}
|
||||
|
||||
export interface NodeCondition extends BaseKubeObjectCondition {
|
||||
/**
|
||||
* Last time we got an update on a given condition.
|
||||
*/
|
||||
lastHeartbeatTime?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* These role label prefixs are the ones that are for master nodes
|
||||
*
|
||||
* The `master` label has been deprecated in Kubernetes 1.20, and will be removed in 1.25 so we
|
||||
* have to also use the newer `control-plane` label
|
||||
*/
|
||||
const masterNodeLabels = ["master", "control-plane"];
|
||||
|
||||
/**
|
||||
* This regex is used in the `getRoleLabels()` method bellow, but placed here
|
||||
* as factoring out regexes is best practice.
|
||||
*/
|
||||
// eslint-disable-next-line xss/no-mixed-html
|
||||
const nodeRoleLabelKeyMatcher = TypedRegEx("^.*node-role.kubernetes.io/+(?<role>.+)$");
|
||||
|
||||
export interface NodeSpec {
|
||||
podCIDR?: string;
|
||||
podCIDRs?: string[];
|
||||
providerID?: string;
|
||||
/**
|
||||
* @deprecated see https://issues.k8s.io/61966
|
||||
*/
|
||||
externalID?: string;
|
||||
taints?: NodeTaint[];
|
||||
unschedulable?: boolean;
|
||||
}
|
||||
|
||||
export interface NodeAddress {
|
||||
type: "Hostname" | "ExternalIP" | "InternalIP";
|
||||
address: string;
|
||||
}
|
||||
|
||||
export interface NodeStatusResources extends Partial<Record<string, string>> {
|
||||
cpu?: string;
|
||||
"ephemeral-storage"?: string;
|
||||
"hugepages-1Gi"?: string;
|
||||
"hugepages-2Mi"?: string;
|
||||
memory?: string;
|
||||
pods?: string;
|
||||
}
|
||||
|
||||
export interface ConfigMapNodeConfigSource {
|
||||
kubeletConfigKey: string;
|
||||
name: string;
|
||||
namespace: string;
|
||||
resourceVersion?: string;
|
||||
uid?: string;
|
||||
}
|
||||
|
||||
export interface NodeConfigSource {
|
||||
configMap?: ConfigMapNodeConfigSource;
|
||||
}
|
||||
|
||||
export interface NodeConfigStatus {
|
||||
active?: NodeConfigSource;
|
||||
assigned?: NodeConfigSource;
|
||||
lastKnownGood?: NodeConfigSource;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface DaemonEndpoint {
|
||||
Port: number; //it must be uppercase for backwards compatibility
|
||||
}
|
||||
|
||||
export interface NodeDaemonEndpoints {
|
||||
kubeletEndpoint?: DaemonEndpoint;
|
||||
}
|
||||
|
||||
export interface ContainerImage {
|
||||
names?: string[];
|
||||
sizeBytes?: number;
|
||||
}
|
||||
|
||||
export interface NodeSystemInfo {
|
||||
architecture: string;
|
||||
bootID: string;
|
||||
containerRuntimeVersion: string;
|
||||
kernelVersion: string;
|
||||
kubeProxyVersion: string;
|
||||
kubeletVersion: string;
|
||||
machineID: string;
|
||||
operatingSystem: string;
|
||||
osImage: string;
|
||||
systemUUID: string;
|
||||
}
|
||||
|
||||
export interface AttachedVolume {
|
||||
name: string;
|
||||
devicePath: string;
|
||||
}
|
||||
|
||||
export interface NodeStatus {
|
||||
capacity?: NodeStatusResources;
|
||||
allocatable?: NodeStatusResources;
|
||||
conditions?: NodeCondition[];
|
||||
addresses?: NodeAddress[];
|
||||
config?: NodeConfigStatus;
|
||||
daemonEndpoints?: NodeDaemonEndpoints;
|
||||
images?: ContainerImage[];
|
||||
nodeInfo?: NodeSystemInfo;
|
||||
phase?: string;
|
||||
volumesInUse?: string[];
|
||||
volumesAttached?: AttachedVolume[];
|
||||
}
|
||||
|
||||
export class Node extends KubeObject<ClusterScopedMetadata, NodeStatus, NodeSpec> {
|
||||
static readonly kind = "Node";
|
||||
|
||||
static readonly namespaced = false;
|
||||
|
||||
static readonly apiBase = "/api/v1/nodes";
|
||||
|
||||
/**
|
||||
* Returns the concatination of all current condition types which have a status
|
||||
* of `"True"`
|
||||
*/
|
||||
getNodeConditionText(): string {
|
||||
if (!this.status?.conditions) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return this.status.conditions
|
||||
.filter((condition) => condition.status === "True")
|
||||
.map((condition) => condition.type)
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
getTaints() {
|
||||
return this.spec.taints || [];
|
||||
}
|
||||
|
||||
isMasterNode(): boolean {
|
||||
return this.getRoleLabelItems().some((roleLabel) => masterNodeLabels.includes(roleLabel));
|
||||
}
|
||||
|
||||
getRoleLabelItems(): string[] {
|
||||
const { labels } = this.metadata;
|
||||
const roleLabels: string[] = [];
|
||||
|
||||
if (!isObject(labels)) {
|
||||
return roleLabels;
|
||||
}
|
||||
|
||||
for (const labelKey of Object.keys(labels)) {
|
||||
const match = nodeRoleLabelKeyMatcher.match(labelKey);
|
||||
|
||||
if (match?.groups) {
|
||||
roleLabels.push(match.groups.role);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof labels["kubernetes.io/role"] === "string") {
|
||||
roleLabels.push(labels["kubernetes.io/role"]);
|
||||
}
|
||||
|
||||
if (typeof labels["node.kubernetes.io/role"] === "string") {
|
||||
roleLabels.push(labels["node.kubernetes.io/role"]);
|
||||
}
|
||||
|
||||
return roleLabels;
|
||||
}
|
||||
|
||||
getRoleLabels(): string {
|
||||
return this.getRoleLabelItems().join(", ");
|
||||
}
|
||||
|
||||
getCpuCapacity() {
|
||||
if (!this.status?.capacity || !this.status.capacity.cpu) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return cpuUnitsToNumber(this.status.capacity.cpu);
|
||||
}
|
||||
|
||||
getMemoryCapacity() {
|
||||
if (!this.status?.capacity || !this.status.capacity.memory) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return unitsToBytes(this.status.capacity.memory);
|
||||
}
|
||||
|
||||
getConditions(): NodeCondition[] {
|
||||
const conditions = this.status?.conditions || [];
|
||||
|
||||
if (this.isUnschedulable()) {
|
||||
return [{ type: "SchedulingDisabled", status: "True" }, ...conditions];
|
||||
}
|
||||
|
||||
return conditions;
|
||||
}
|
||||
|
||||
getActiveConditions() {
|
||||
return this.getConditions().filter((c) => c.status === "True");
|
||||
}
|
||||
|
||||
getWarningConditions() {
|
||||
const goodConditions = ["Ready", "HostUpgrades", "SchedulingDisabled"];
|
||||
|
||||
return this.getActiveConditions().filter((condition) => {
|
||||
return !goodConditions.includes(condition.type);
|
||||
});
|
||||
}
|
||||
|
||||
getKubeletVersion() {
|
||||
return this.status?.nodeInfo?.kubeletVersion ?? "<unknown>";
|
||||
}
|
||||
|
||||
getOperatingSystem(): string {
|
||||
return (
|
||||
this.metadata?.labels?.["kubernetes.io/os"] ||
|
||||
this.metadata?.labels?.["beta.kubernetes.io/os"] ||
|
||||
this.status?.nodeInfo?.operatingSystem ||
|
||||
"linux"
|
||||
);
|
||||
}
|
||||
|
||||
isUnschedulable() {
|
||||
return this.spec.unschedulable;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { Pod } from "./pod";
|
||||
import { object } from "@k8slens/utilities";
|
||||
import type { TypedLocalObjectReference, LabelSelector, NamespaceScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import type { ResourceRequirements } from "../types/resource-requirements";
|
||||
|
||||
export interface PersistentVolumeClaimSpec {
|
||||
accessModes?: string[];
|
||||
dataSource?: TypedLocalObjectReference;
|
||||
dataSourceRef?: TypedLocalObjectReference;
|
||||
resources?: ResourceRequirements;
|
||||
selector?: LabelSelector;
|
||||
storageClassName?: string;
|
||||
volumeMode?: string;
|
||||
volumeName?: string;
|
||||
}
|
||||
|
||||
export interface PersistentVolumeClaimStatus {
|
||||
phase: string; // Pending
|
||||
}
|
||||
|
||||
export class PersistentVolumeClaim extends KubeObject<
|
||||
NamespaceScopedMetadata,
|
||||
PersistentVolumeClaimStatus,
|
||||
PersistentVolumeClaimSpec
|
||||
> {
|
||||
static readonly kind = "PersistentVolumeClaim";
|
||||
|
||||
static readonly namespaced = true;
|
||||
|
||||
static readonly apiBase = "/api/v1/persistentvolumeclaims";
|
||||
|
||||
getPods(pods: Pod[]): Pod[] {
|
||||
return pods
|
||||
.filter((pod) => pod.getNs() === this.getNs())
|
||||
.filter(
|
||||
(pod) =>
|
||||
pod.getVolumes().filter((volume) => volume.persistentVolumeClaim?.claimName === this.getName()).length > 0,
|
||||
);
|
||||
}
|
||||
|
||||
getStorage(): string {
|
||||
return this.spec.resources?.requests?.storage ?? "-";
|
||||
}
|
||||
|
||||
getMatchLabels(): string[] {
|
||||
return object.entries(this.spec.selector?.matchLabels).map(([name, val]) => `${name}:${val}`);
|
||||
}
|
||||
|
||||
getMatchExpressions() {
|
||||
return this.spec.selector?.matchExpressions ?? [];
|
||||
}
|
||||
|
||||
getStatus(): string {
|
||||
return this.status?.phase ?? "-";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { unitsToBytes } from "@k8slens/utilities";
|
||||
import type { TypedLocalObjectReference, LabelSelector, ObjectReference, ClusterScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import type { ResourceRequirements } from "../types/resource-requirements";
|
||||
|
||||
export interface PersistentVolumeSpec {
|
||||
/**
|
||||
* AccessModes contains the desired access modes the volume should have.
|
||||
*
|
||||
* More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1
|
||||
*/
|
||||
accessModes?: string[];
|
||||
dataSource?: TypedLocalObjectReference;
|
||||
dataSourceRef?: TypedLocalObjectReference;
|
||||
resources?: ResourceRequirements;
|
||||
selector?: LabelSelector;
|
||||
|
||||
/**
|
||||
* Name of the StorageClass required by the claim.
|
||||
*
|
||||
* More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1
|
||||
*/
|
||||
storageClassName?: string;
|
||||
|
||||
/**
|
||||
* Defines what type of volume is required by the claim. Value of Filesystem is implied when not
|
||||
* included in claim spec.
|
||||
*/
|
||||
volumeMode?: string;
|
||||
|
||||
/**
|
||||
* A description of the persistent volume\'s resources and capacity.
|
||||
*
|
||||
* More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#capacity
|
||||
*/
|
||||
capacity?: Partial<Record<string, string>>;
|
||||
flexVolume?: {
|
||||
driver: string; // ceph.rook.io/rook-ceph-system,
|
||||
options?: {
|
||||
clusterNamespace: string; // rook-ceph,
|
||||
image: string; // pvc-c5d7c485-9f1b-11e8-b0ea-9600000e54fb,
|
||||
pool: string; // replicapool,
|
||||
storageClass: string; // rook-ceph-block
|
||||
};
|
||||
};
|
||||
mountOptions?: string[];
|
||||
claimRef?: ObjectReference;
|
||||
persistentVolumeReclaimPolicy?: string; // Delete,
|
||||
nfs?: {
|
||||
path: string;
|
||||
server: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface PersistentVolumeStatus {
|
||||
phase: string;
|
||||
reason?: string;
|
||||
}
|
||||
|
||||
export class PersistentVolume extends KubeObject<ClusterScopedMetadata, PersistentVolumeStatus, PersistentVolumeSpec> {
|
||||
static kind = "PersistentVolume";
|
||||
|
||||
static namespaced = false;
|
||||
|
||||
static apiBase = "/api/v1/persistentvolumes";
|
||||
|
||||
getCapacity(inBytes = false) {
|
||||
const capacity = this.spec.capacity;
|
||||
|
||||
if (capacity?.storage) {
|
||||
if (inBytes) {
|
||||
return unitsToBytes(capacity.storage);
|
||||
}
|
||||
|
||||
return capacity.storage;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
getStatus() {
|
||||
return this.status?.phase || "-";
|
||||
}
|
||||
|
||||
getStorageClass(): string {
|
||||
return this.spec.storageClassName ?? "";
|
||||
}
|
||||
|
||||
getClaimRefName(): string {
|
||||
return this.spec.claimRef?.name ?? "";
|
||||
}
|
||||
|
||||
getStorageClassName() {
|
||||
return this.spec.storageClassName || "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { LabelSelector, NamespaceScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import type { Condition } from "../types/condition";
|
||||
|
||||
export interface V1Beta1PodDisruptionBudgetSpec {
|
||||
minAvailable: string;
|
||||
maxUnavailable: string;
|
||||
selector: LabelSelector;
|
||||
}
|
||||
|
||||
export interface V1PodDisruptionBudgetSpec {
|
||||
maxUnavailable?: string | number;
|
||||
minAvailable?: string | number;
|
||||
selector?: LabelSelector;
|
||||
}
|
||||
|
||||
export type PodDisruptionBudgetSpec = V1Beta1PodDisruptionBudgetSpec | V1PodDisruptionBudgetSpec;
|
||||
|
||||
export interface V1Beta1PodDisruptionBudgetStatus {
|
||||
currentHealthy: number;
|
||||
desiredHealthy: number;
|
||||
disruptionsAllowed: number;
|
||||
expectedPods: number;
|
||||
}
|
||||
|
||||
export interface V1PodDisruptionBudgetStatus {
|
||||
conditions?: Condition[];
|
||||
currentHealthy: number;
|
||||
desiredHealthy: number;
|
||||
disruptedPods?: Partial<Record<string, string>>;
|
||||
disruptionsAllowed: number;
|
||||
expectedPods: number;
|
||||
observedGeneration?: number;
|
||||
}
|
||||
|
||||
export type PodDisruptionBudgetStatus = V1Beta1PodDisruptionBudgetStatus | V1PodDisruptionBudgetStatus;
|
||||
|
||||
export class PodDisruptionBudget extends KubeObject<
|
||||
NamespaceScopedMetadata,
|
||||
PodDisruptionBudgetStatus,
|
||||
PodDisruptionBudgetSpec
|
||||
> {
|
||||
static readonly kind = "PodDisruptionBudget";
|
||||
|
||||
static readonly namespaced = true;
|
||||
|
||||
static readonly apiBase = "/apis/policy/v1beta1/poddisruptionbudgets";
|
||||
|
||||
getSelectors() {
|
||||
return KubeObject.stringifyLabels(this.spec.selector?.matchLabels);
|
||||
}
|
||||
|
||||
getMinAvailable() {
|
||||
return this.spec.minAvailable ?? "N/A";
|
||||
}
|
||||
|
||||
getMaxUnavailable() {
|
||||
return this.spec.maxUnavailable ?? "N/A";
|
||||
}
|
||||
|
||||
getCurrentHealthy() {
|
||||
return this.status?.currentHealthy ?? 0;
|
||||
}
|
||||
|
||||
getDesiredHealthy() {
|
||||
return this.status?.desiredHealthy ?? 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { KubeJsonApiData, KubeObjectMetadata, KubeObjectScope, NamespaceScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
|
||||
export interface PodMetricsData extends KubeJsonApiData<KubeObjectMetadata<KubeObjectScope.Namespace>, void, void> {
|
||||
timestamp: string;
|
||||
window: string;
|
||||
containers: PodMetricsContainer[];
|
||||
}
|
||||
|
||||
export interface PodMetricsContainerUsage {
|
||||
cpu: string;
|
||||
memory: string;
|
||||
}
|
||||
|
||||
export interface PodMetricsContainer {
|
||||
name: string;
|
||||
usage: PodMetricsContainerUsage;
|
||||
}
|
||||
|
||||
export class PodMetrics extends KubeObject<NamespaceScopedMetadata, void, void> {
|
||||
static readonly kind = "PodMetrics";
|
||||
|
||||
static readonly namespaced = true;
|
||||
|
||||
static readonly apiBase = "/apis/metrics.k8s.io/v1beta1/pods";
|
||||
|
||||
timestamp: string;
|
||||
|
||||
window: string;
|
||||
|
||||
containers: PodMetricsContainer[];
|
||||
|
||||
constructor({ timestamp, window, containers, ...rest }: PodMetricsData) {
|
||||
super(rest);
|
||||
this.timestamp = timestamp;
|
||||
this.window = window;
|
||||
this.containers = containers;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { ClusterScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
|
||||
export interface PodSecurityPolicySpec {
|
||||
allowPrivilegeEscalation?: boolean;
|
||||
allowedCSIDrivers?: {
|
||||
name: string;
|
||||
}[];
|
||||
allowedCapabilities: string[];
|
||||
allowedFlexVolumes?: {
|
||||
driver: string;
|
||||
}[];
|
||||
allowedHostPaths?: {
|
||||
pathPrefix: string;
|
||||
readOnly: boolean;
|
||||
}[];
|
||||
allowedProcMountTypes?: string[];
|
||||
allowedUnsafeSysctls?: string[];
|
||||
defaultAddCapabilities?: string[];
|
||||
defaultAllowPrivilegeEscalation?: boolean;
|
||||
forbiddenSysctls?: string[];
|
||||
fsGroup?: {
|
||||
rule: string;
|
||||
ranges: {
|
||||
max: number;
|
||||
min: number;
|
||||
}[];
|
||||
};
|
||||
hostIPC?: boolean;
|
||||
hostNetwork?: boolean;
|
||||
hostPID?: boolean;
|
||||
hostPorts?: {
|
||||
max: number;
|
||||
min: number;
|
||||
}[];
|
||||
privileged?: boolean;
|
||||
readOnlyRootFilesystem?: boolean;
|
||||
requiredDropCapabilities?: string[];
|
||||
runAsGroup?: {
|
||||
ranges: {
|
||||
max: number;
|
||||
min: number;
|
||||
}[];
|
||||
rule: string;
|
||||
};
|
||||
runAsUser?: {
|
||||
rule: string;
|
||||
ranges: {
|
||||
max: number;
|
||||
min: number;
|
||||
}[];
|
||||
};
|
||||
runtimeClass?: {
|
||||
allowedRuntimeClassNames: string[];
|
||||
defaultRuntimeClassName: string;
|
||||
};
|
||||
seLinux?: {
|
||||
rule: string;
|
||||
seLinuxOptions: {
|
||||
level: string;
|
||||
role: string;
|
||||
type: string;
|
||||
user: string;
|
||||
};
|
||||
};
|
||||
supplementalGroups?: {
|
||||
rule: string;
|
||||
ranges: {
|
||||
max: number;
|
||||
min: number;
|
||||
}[];
|
||||
};
|
||||
volumes?: string[];
|
||||
}
|
||||
|
||||
export class PodSecurityPolicy extends KubeObject<ClusterScopedMetadata, void, PodSecurityPolicySpec> {
|
||||
static readonly kind = "PodSecurityPolicy";
|
||||
|
||||
static readonly namespaced = false;
|
||||
|
||||
static readonly apiBase = "/apis/policy/v1beta1/podsecuritypolicies";
|
||||
|
||||
isPrivileged() {
|
||||
return !!this.spec.privileged;
|
||||
}
|
||||
|
||||
getVolumes() {
|
||||
return this.spec.volumes || [];
|
||||
}
|
||||
|
||||
getRules() {
|
||||
const { fsGroup, runAsGroup, runAsUser, supplementalGroups, seLinux } = this.spec;
|
||||
|
||||
return {
|
||||
fsGroup: fsGroup ? fsGroup.rule : "",
|
||||
runAsGroup: runAsGroup ? runAsGroup.rule : "",
|
||||
runAsUser: runAsUser ? runAsUser.rule : "",
|
||||
supplementalGroups: supplementalGroups ? supplementalGroups.rule : "",
|
||||
seLinux: seLinux ? seLinux.rule : "",
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import assert from "assert";
|
||||
import type { Container } from "../types/container";
|
||||
import { Pod, type PodContainerStatus } from "./pod";
|
||||
|
||||
interface GetDummyPodOptions {
|
||||
running?: number;
|
||||
dead?: number;
|
||||
initRunning?: number;
|
||||
initDead?: number;
|
||||
}
|
||||
|
||||
function getDummyPod(rawOpts: GetDummyPodOptions = {}): Pod {
|
||||
const { running = 0, dead = 0, initDead = 0, initRunning = 0 } = rawOpts;
|
||||
|
||||
const containers: Container[] = [];
|
||||
const initContainers: Container[] = [];
|
||||
const containerStatuses: PodContainerStatus[] = [];
|
||||
const initContainerStatuses: PodContainerStatus[] = [];
|
||||
const pod = new Pod({
|
||||
apiVersion: "v1",
|
||||
kind: "Pod",
|
||||
metadata: {
|
||||
uid: "1",
|
||||
name: "test",
|
||||
resourceVersion: "v1",
|
||||
namespace: "default",
|
||||
selfLink: "/api/v1/pods/default/test",
|
||||
},
|
||||
spec: {
|
||||
containers,
|
||||
initContainers,
|
||||
serviceAccount: "dummy",
|
||||
serviceAccountName: "dummy",
|
||||
},
|
||||
status: {
|
||||
phase: "Running",
|
||||
conditions: [],
|
||||
hostIP: "10.0.0.1",
|
||||
podIP: "10.0.0.1",
|
||||
startTime: "now",
|
||||
containerStatuses,
|
||||
initContainerStatuses,
|
||||
},
|
||||
});
|
||||
|
||||
for (let i = 0; i < running; i += 1) {
|
||||
const name = `container_running_${i}`;
|
||||
|
||||
containers.push({
|
||||
image: "dummy",
|
||||
imagePullPolicy: "Always",
|
||||
name,
|
||||
});
|
||||
containerStatuses.push({
|
||||
image: "dummy",
|
||||
imageID: "dummy",
|
||||
name,
|
||||
ready: true,
|
||||
restartCount: i,
|
||||
state: {
|
||||
running: {
|
||||
startedAt: "before",
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
for (let i = 0; i < dead; i += 1) {
|
||||
const name = `container_dead_${i}`;
|
||||
|
||||
containers.push({
|
||||
image: "dummy",
|
||||
imagePullPolicy: "Always",
|
||||
name,
|
||||
});
|
||||
containerStatuses.push({
|
||||
image: "dummy",
|
||||
imageID: "dummy",
|
||||
name,
|
||||
ready: false,
|
||||
restartCount: i,
|
||||
state: {
|
||||
terminated: {
|
||||
startedAt: "before",
|
||||
exitCode: i + 1,
|
||||
finishedAt: "later",
|
||||
reason: `reason_${i}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
for (let i = 0; i < initRunning; i += 1) {
|
||||
const name = `container_init-running_${i}`;
|
||||
|
||||
initContainers.push({
|
||||
image: "dummy",
|
||||
imagePullPolicy: "Always",
|
||||
name,
|
||||
});
|
||||
initContainerStatuses.push({
|
||||
image: "dummy",
|
||||
imageID: "dummy",
|
||||
name,
|
||||
ready: true,
|
||||
restartCount: i,
|
||||
state: {
|
||||
running: {
|
||||
startedAt: "before",
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
for (let i = 0; i < initDead; i += 1) {
|
||||
const name = `container_init-dead_${i}`;
|
||||
|
||||
initContainers.push({
|
||||
image: "dummy",
|
||||
imagePullPolicy: "Always",
|
||||
name,
|
||||
});
|
||||
initContainerStatuses.push({
|
||||
image: "dummy",
|
||||
imageID: "dummy",
|
||||
name,
|
||||
ready: false,
|
||||
restartCount: i,
|
||||
state: {
|
||||
terminated: {
|
||||
startedAt: "before",
|
||||
exitCode: i + 1,
|
||||
finishedAt: "later",
|
||||
reason: `reason_${i}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return pod;
|
||||
}
|
||||
|
||||
describe("Pods", () => {
|
||||
const podTests = [];
|
||||
|
||||
for (let r = 0; r < 3; r += 1) {
|
||||
for (let d = 0; d < 3; d += 1) {
|
||||
for (let ir = 0; ir < 3; ir += 1) {
|
||||
for (let id = 0; id < 3; id += 1) {
|
||||
podTests.push([r, d, ir, id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
describe.each(podTests)(
|
||||
"for [%d running, %d dead] & initial [%d running, %d dead]",
|
||||
(running, dead, initRunning, initDead) => {
|
||||
const pod = getDummyPod({ running, dead, initRunning, initDead });
|
||||
|
||||
function getNamedContainer(name: string) {
|
||||
return {
|
||||
image: "dummy",
|
||||
imagePullPolicy: "Always",
|
||||
name,
|
||||
};
|
||||
}
|
||||
|
||||
it("getRunningContainers should return only running and init running", () => {
|
||||
const res = [
|
||||
...Array.from(new Array(running), (val, index) => getNamedContainer(`container_running_${index}`)),
|
||||
...Array.from(new Array(initRunning), (val, index) => getNamedContainer(`container_init-running_${index}`)),
|
||||
];
|
||||
|
||||
expect(pod.getRunningContainers()).toStrictEqual(res);
|
||||
});
|
||||
|
||||
it("getAllContainers should return all containers", () => {
|
||||
const res = [
|
||||
...Array.from(new Array(running), (val, index) => getNamedContainer(`container_running_${index}`)),
|
||||
...Array.from(new Array(dead), (val, index) => getNamedContainer(`container_dead_${index}`)),
|
||||
...Array.from(new Array(initRunning), (val, index) => getNamedContainer(`container_init-running_${index}`)),
|
||||
...Array.from(new Array(initDead), (val, index) => getNamedContainer(`container_init-dead_${index}`)),
|
||||
];
|
||||
|
||||
expect(pod.getAllContainers()).toStrictEqual(res);
|
||||
});
|
||||
|
||||
it("getRestartsCount should return total restart counts", () => {
|
||||
function sum(len: number): number {
|
||||
let res = 0;
|
||||
|
||||
for (let i = 0; i < len; i += 1) {
|
||||
res += i;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
expect(pod.getRestartsCount()).toStrictEqual(sum(running) + sum(dead));
|
||||
});
|
||||
|
||||
it("hasIssues should return false", () => {
|
||||
expect(pod.hasIssues()).toStrictEqual(false);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
describe("getSelectedNodeOs", () => {
|
||||
it("should return stable", () => {
|
||||
const pod = getDummyPod();
|
||||
|
||||
pod.spec.nodeSelector = {
|
||||
"kubernetes.io/os": "foobar",
|
||||
};
|
||||
|
||||
expect(pod.getSelectedNodeOs()).toStrictEqual("foobar");
|
||||
});
|
||||
|
||||
it("should return beta", () => {
|
||||
const pod = getDummyPod();
|
||||
|
||||
pod.spec.nodeSelector = {
|
||||
"beta.kubernetes.io/os": "foobar1",
|
||||
};
|
||||
|
||||
expect(pod.getSelectedNodeOs()).toStrictEqual("foobar1");
|
||||
});
|
||||
|
||||
it("should return stable over beta", () => {
|
||||
const pod = getDummyPod();
|
||||
|
||||
pod.spec.nodeSelector = {
|
||||
"kubernetes.io/os": "foobar2",
|
||||
"beta.kubernetes.io/os": "foobar3",
|
||||
};
|
||||
|
||||
expect(pod.getSelectedNodeOs()).toStrictEqual("foobar2");
|
||||
});
|
||||
|
||||
it("should return undefined if none set", () => {
|
||||
const pod = getDummyPod();
|
||||
|
||||
expect(pod.getSelectedNodeOs()).toStrictEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe("hasIssues", () => {
|
||||
it("should return true if a condition isn't ready", () => {
|
||||
const pod = getDummyPod({ running: 1 });
|
||||
|
||||
pod.status?.conditions.push({
|
||||
type: "Ready",
|
||||
status: "foobar",
|
||||
lastProbeTime: 1,
|
||||
lastTransitionTime: "longer ago",
|
||||
});
|
||||
|
||||
expect(pod.hasIssues()).toStrictEqual(true);
|
||||
});
|
||||
|
||||
it("should return false if a condition is non-ready", () => {
|
||||
const pod = getDummyPod({ running: 1 });
|
||||
|
||||
pod.status?.conditions.push({
|
||||
type: "dummy",
|
||||
status: "foobar",
|
||||
lastProbeTime: 1,
|
||||
lastTransitionTime: "longer ago",
|
||||
});
|
||||
|
||||
expect(pod.hasIssues()).toStrictEqual(false);
|
||||
});
|
||||
|
||||
it("should return true if a current container is in a crash loop back off", () => {
|
||||
const pod = getDummyPod({ running: 1 });
|
||||
const firstStatus = pod.status?.containerStatuses?.[0];
|
||||
|
||||
assert(firstStatus);
|
||||
|
||||
firstStatus.state = {
|
||||
waiting: {
|
||||
reason: "CrashLookBackOff",
|
||||
message: "too much foobar",
|
||||
},
|
||||
};
|
||||
|
||||
expect(pod.hasIssues()).toStrictEqual(true);
|
||||
});
|
||||
|
||||
it("should return true if a current phase isn't running", () => {
|
||||
const pod = getDummyPod({ running: 1 });
|
||||
|
||||
assert(pod.status);
|
||||
|
||||
pod.status.phase = "not running";
|
||||
|
||||
expect(pod.hasIssues()).toStrictEqual(true);
|
||||
});
|
||||
|
||||
it("should return false if a current phase is running", () => {
|
||||
const pod = getDummyPod({ running: 1 });
|
||||
|
||||
assert(pod.status);
|
||||
|
||||
pod.status.phase = "Running";
|
||||
|
||||
expect(pod.hasIssues()).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,834 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { isDefined } from "@k8slens/utilities";
|
||||
import type { RequireExactlyOne } from "type-fest";
|
||||
import type { PersistentVolumeClaimSpec } from "./persistent-volume-claim";
|
||||
import type {
|
||||
Affinity,
|
||||
KubeObjectMetadata,
|
||||
LocalObjectReference,
|
||||
NamespaceScopedMetadata,
|
||||
Toleration,
|
||||
} from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import type { Container, ObjectFieldSelector, PodSecurityContext, Probe, ResourceFieldSelector } from "../types";
|
||||
import type { SecretReference } from "./secret";
|
||||
|
||||
// Reference: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#read-log-pod-v1-core
|
||||
export interface PodLogsQuery {
|
||||
container?: string;
|
||||
tailLines?: number;
|
||||
timestamps?: boolean;
|
||||
sinceTime?: string; // Date.toISOString()-format
|
||||
follow?: boolean;
|
||||
previous?: boolean;
|
||||
}
|
||||
|
||||
export enum PodStatusPhase {
|
||||
TERMINATED = "Terminated",
|
||||
FAILED = "Failed",
|
||||
PENDING = "Pending",
|
||||
RUNNING = "Running",
|
||||
SUCCEEDED = "Succeeded",
|
||||
EVICTED = "Evicted",
|
||||
}
|
||||
|
||||
export interface ContainerStateRunning {
|
||||
startedAt: string;
|
||||
}
|
||||
|
||||
export interface ContainerStateWaiting {
|
||||
reason: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface ContainerStateTerminated {
|
||||
startedAt: string;
|
||||
finishedAt: string;
|
||||
exitCode: number;
|
||||
reason: string;
|
||||
containerID?: string;
|
||||
message?: string;
|
||||
signal?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* ContainerState holds a possible state of container. Only one of its members
|
||||
* may be specified. If none of them is specified, the default one is
|
||||
* `ContainerStateWaiting`.
|
||||
*/
|
||||
export interface ContainerState {
|
||||
running?: ContainerStateRunning;
|
||||
waiting?: ContainerStateWaiting;
|
||||
terminated?: ContainerStateTerminated;
|
||||
}
|
||||
|
||||
export type ContainerStateValues = Partial<ContainerState[keyof ContainerState]>;
|
||||
|
||||
export interface PodContainerStatus {
|
||||
name: string;
|
||||
state?: ContainerState;
|
||||
lastState?: ContainerState;
|
||||
ready: boolean;
|
||||
restartCount: number;
|
||||
image: string;
|
||||
imageID: string;
|
||||
containerID?: string;
|
||||
started?: boolean;
|
||||
}
|
||||
|
||||
export interface AwsElasticBlockStoreSource {
|
||||
volumeID: string;
|
||||
fsType: string;
|
||||
}
|
||||
|
||||
export interface AzureDiskSource {
|
||||
/**
|
||||
* The name of the VHD blob object OR the name of an Azure managed data disk if `kind` is `"Managed"`.
|
||||
*/
|
||||
diskName: string;
|
||||
/**
|
||||
* The URI of the vhd blob object OR the `resourceID` of an Azure managed data disk if `kind` is `"Managed"`.
|
||||
*/
|
||||
diskURI: string;
|
||||
/**
|
||||
* Kind of disk
|
||||
* @default "Shared"
|
||||
*/
|
||||
kind?: "Shared" | "Dedicated" | "Managed";
|
||||
/**
|
||||
* Disk caching mode.
|
||||
* @default "None"
|
||||
*/
|
||||
cachingMode?: "None" | "ReadOnly" | "ReadWrite";
|
||||
/**
|
||||
* The filesystem type to mount.
|
||||
* @default "ext4"
|
||||
*/
|
||||
fsType?: string;
|
||||
/**
|
||||
* Whether the filesystem is used as readOnly.
|
||||
* @default false
|
||||
*/
|
||||
readonly?: boolean;
|
||||
}
|
||||
|
||||
export interface AzureFileSource {
|
||||
/**
|
||||
* The name of the secret that contains both Azure storage account name and key.
|
||||
*/
|
||||
secretName: string;
|
||||
/**
|
||||
* The share name to be used.
|
||||
*/
|
||||
shareName: string;
|
||||
/**
|
||||
* In case the secret is stored in a different namespace.
|
||||
* @default "default"
|
||||
*/
|
||||
secretNamespace?: string;
|
||||
/**
|
||||
* Whether the filesystem is used as readOnly.
|
||||
*/
|
||||
readOnly: boolean;
|
||||
}
|
||||
|
||||
export interface CephfsSource {
|
||||
/**
|
||||
* List of Ceph monitors
|
||||
*/
|
||||
monitors: string[];
|
||||
/**
|
||||
* Used as the mounted root, rather than the full Ceph tree.
|
||||
* @default "/"
|
||||
*/
|
||||
path?: string;
|
||||
/**
|
||||
* The RADOS user name.
|
||||
* @default "admin"
|
||||
*/
|
||||
user?: string;
|
||||
/**
|
||||
* The path to the keyring file.
|
||||
* @default "/etc/ceph/user.secret"
|
||||
*/
|
||||
secretFile?: string;
|
||||
/**
|
||||
* Reference to Ceph authentication secrets. If provided, then the secret overrides `secretFile`
|
||||
*/
|
||||
secretRef?: SecretReference;
|
||||
/**
|
||||
* Whether the filesystem is used as readOnly.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
readOnly?: boolean;
|
||||
}
|
||||
|
||||
export interface CinderSource {
|
||||
volumeID: string;
|
||||
fsType: string;
|
||||
/**
|
||||
* @default false
|
||||
*/
|
||||
readOnly?: boolean;
|
||||
secretRef?: SecretReference;
|
||||
}
|
||||
|
||||
export interface ConfigMapSource {
|
||||
name: string;
|
||||
items: {
|
||||
key: string;
|
||||
path: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface DownwardApiSource {
|
||||
items: {
|
||||
path: string;
|
||||
fieldRef: {
|
||||
fieldPath: string;
|
||||
};
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface EphemeralSource {
|
||||
volumeClaimTemplate: {
|
||||
/**
|
||||
* All the rest of the fields are ignored and rejected during validation
|
||||
*/
|
||||
metadata?: Pick<KubeObjectMetadata, "labels" | "annotations">;
|
||||
spec: PersistentVolumeClaimSpec;
|
||||
};
|
||||
}
|
||||
|
||||
export interface EmptyDirSource {
|
||||
medium?: string;
|
||||
sizeLimit?: string;
|
||||
}
|
||||
|
||||
export interface FiberChannelSource {
|
||||
/**
|
||||
* A list of World Wide Names
|
||||
*/
|
||||
targetWWNs: string[];
|
||||
/**
|
||||
* Logical Unit number
|
||||
*/
|
||||
lun: number;
|
||||
/**
|
||||
* The type of filesystem
|
||||
* @default "ext4"
|
||||
*/
|
||||
fsType?: string;
|
||||
readOnly: boolean;
|
||||
}
|
||||
|
||||
export interface FlockerSource {
|
||||
datasetName: string;
|
||||
}
|
||||
|
||||
export interface FlexVolumeSource {
|
||||
driver: string;
|
||||
fsType?: string;
|
||||
secretRef?: LocalObjectReference;
|
||||
/**
|
||||
* @default false
|
||||
*/
|
||||
readOnly?: boolean;
|
||||
options?: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface GcePersistentDiskSource {
|
||||
pdName: string;
|
||||
fsType: string;
|
||||
}
|
||||
|
||||
export interface GitRepoSource {
|
||||
repository: string;
|
||||
revision: string;
|
||||
}
|
||||
|
||||
export interface GlusterFsSource {
|
||||
/**
|
||||
* The name of the Endpoints object that represents a Gluster cluster configuration.
|
||||
*/
|
||||
endpoints: string;
|
||||
/**
|
||||
* The Glusterfs volume name.
|
||||
*/
|
||||
path: string;
|
||||
/**
|
||||
* The boolean that sets the mountpoint readOnly or readWrite.
|
||||
*/
|
||||
readOnly: boolean;
|
||||
}
|
||||
|
||||
export interface HostPathSource {
|
||||
path: string;
|
||||
/**
|
||||
* Determines the sorts of checks that will be done
|
||||
* @default ""
|
||||
*/
|
||||
type?: "" | "DirectoryOrCreate" | "Directory" | "FileOrCreate" | "File" | "Socket" | "CharDevice" | "BlockDevice";
|
||||
}
|
||||
|
||||
export interface IScsiSource {
|
||||
targetPortal: string;
|
||||
iqn: string;
|
||||
lun: number;
|
||||
fsType: string;
|
||||
readOnly: boolean;
|
||||
chapAuthDiscovery?: boolean;
|
||||
chapAuthSession?: boolean;
|
||||
secretRef?: SecretReference;
|
||||
}
|
||||
|
||||
export interface LocalSource {
|
||||
path: string;
|
||||
}
|
||||
|
||||
export interface NetworkFsSource {
|
||||
server: string;
|
||||
path: string;
|
||||
readOnly?: boolean;
|
||||
}
|
||||
|
||||
export interface PersistentVolumeClaimSource {
|
||||
claimName: string;
|
||||
}
|
||||
|
||||
export interface PhotonPersistentDiskSource {
|
||||
pdID: string;
|
||||
/**
|
||||
* @default "ext4"
|
||||
*/
|
||||
fsType?: string;
|
||||
}
|
||||
|
||||
export interface PortworxVolumeSource {
|
||||
volumeID: string;
|
||||
fsType?: string;
|
||||
readOnly?: boolean;
|
||||
}
|
||||
|
||||
export interface KeyToPath {
|
||||
key: string;
|
||||
path: string;
|
||||
mode?: number;
|
||||
}
|
||||
|
||||
export interface ConfigMapProjection {
|
||||
name: string;
|
||||
items?: KeyToPath[];
|
||||
optional?: boolean;
|
||||
}
|
||||
|
||||
export interface DownwardAPIVolumeFile {
|
||||
path: string;
|
||||
fieldRef?: ObjectFieldSelector;
|
||||
resourceFieldRef?: ResourceFieldSelector;
|
||||
mode?: number;
|
||||
}
|
||||
|
||||
export interface DownwardAPIProjection {
|
||||
items?: DownwardAPIVolumeFile[];
|
||||
}
|
||||
|
||||
export interface SecretProjection {
|
||||
name: string;
|
||||
items?: KeyToPath[];
|
||||
optional?: boolean;
|
||||
}
|
||||
|
||||
export interface ServiceAccountTokenProjection {
|
||||
audience?: string;
|
||||
expirationSeconds?: number;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export interface VolumeProjection {
|
||||
secret?: SecretProjection;
|
||||
downwardAPI?: DownwardAPIProjection;
|
||||
configMap?: ConfigMapProjection;
|
||||
serviceAccountToken?: ServiceAccountTokenProjection;
|
||||
}
|
||||
|
||||
export interface ProjectedSource {
|
||||
sources?: VolumeProjection[];
|
||||
defaultMode?: number;
|
||||
}
|
||||
|
||||
export interface QuobyteSource {
|
||||
registry: string;
|
||||
volume: string;
|
||||
/**
|
||||
* @default false
|
||||
*/
|
||||
readOnly?: boolean;
|
||||
/**
|
||||
* @default "serivceaccount"
|
||||
*/
|
||||
user?: string;
|
||||
group?: string;
|
||||
tenant?: string;
|
||||
}
|
||||
|
||||
export interface RadosBlockDeviceSource {
|
||||
monitors: string[];
|
||||
image: string;
|
||||
/**
|
||||
* @default "ext4"
|
||||
*/
|
||||
fsType?: string;
|
||||
/**
|
||||
* @default "rbd"
|
||||
*/
|
||||
pool?: string;
|
||||
/**
|
||||
* @default "admin"
|
||||
*/
|
||||
user?: string;
|
||||
/**
|
||||
* @default "/etc/ceph/keyring"
|
||||
*/
|
||||
keyring?: string;
|
||||
secretRef?: SecretReference;
|
||||
/**
|
||||
* @default false
|
||||
*/
|
||||
readOnly?: boolean;
|
||||
}
|
||||
|
||||
export interface ScaleIoSource {
|
||||
gateway: string;
|
||||
system: string;
|
||||
secretRef?: LocalObjectReference;
|
||||
/**
|
||||
* @default false
|
||||
*/
|
||||
sslEnabled?: boolean;
|
||||
protectionDomain?: string;
|
||||
storagePool?: string;
|
||||
/**
|
||||
* @default "ThinProvisioned"
|
||||
*/
|
||||
storageMode?: "ThickProvisioned" | "ThinProvisioned";
|
||||
volumeName: string;
|
||||
/**
|
||||
* @default "xfs"
|
||||
*/
|
||||
fsType?: string;
|
||||
/**
|
||||
* @default false
|
||||
*/
|
||||
readOnly?: boolean;
|
||||
}
|
||||
|
||||
export interface SecretSource {
|
||||
secretName: string;
|
||||
items?: {
|
||||
key: string;
|
||||
path: string;
|
||||
mode?: number;
|
||||
}[];
|
||||
defaultMode?: number;
|
||||
optional?: boolean;
|
||||
}
|
||||
|
||||
export interface StorageOsSource {
|
||||
volumeName: string;
|
||||
/**
|
||||
* @default Pod.metadata.namespace
|
||||
*/
|
||||
volumeNamespace?: string;
|
||||
/**
|
||||
* @default "ext4"
|
||||
*/
|
||||
fsType?: string;
|
||||
/**
|
||||
* @default false
|
||||
*/
|
||||
readOnly?: boolean;
|
||||
secretRef?: LocalObjectReference;
|
||||
}
|
||||
|
||||
export interface VsphereVolumeSource {
|
||||
volumePath: string;
|
||||
/**
|
||||
* @default "ext4"
|
||||
*/
|
||||
fsType?: string;
|
||||
storagePolicyName?: string;
|
||||
storagePolicyID?: string;
|
||||
}
|
||||
|
||||
export interface ContainerStorageInterfaceSource {
|
||||
driver: string;
|
||||
/**
|
||||
* @default false
|
||||
*/
|
||||
readOnly?: boolean;
|
||||
/**
|
||||
* @default "ext4"
|
||||
*/
|
||||
fsType?: string;
|
||||
volumeAttributes?: Record<string, string>;
|
||||
controllerPublishSecretRef?: SecretReference;
|
||||
nodeStageSecretRef?: SecretReference;
|
||||
nodePublishSecretRef?: SecretReference;
|
||||
controllerExpandSecretRef?: SecretReference;
|
||||
}
|
||||
|
||||
export interface PodVolumeVariants {
|
||||
awsElasticBlockStore: AwsElasticBlockStoreSource;
|
||||
azureDisk: AzureDiskSource;
|
||||
azureFile: AzureFileSource;
|
||||
cephfs: CephfsSource;
|
||||
cinder: CinderSource;
|
||||
configMap: ConfigMapSource;
|
||||
csi: ContainerStorageInterfaceSource;
|
||||
downwardAPI: DownwardApiSource;
|
||||
emptyDir: EmptyDirSource;
|
||||
ephemeral: EphemeralSource;
|
||||
fc: FiberChannelSource;
|
||||
flexVolume: FlexVolumeSource;
|
||||
flocker: FlockerSource;
|
||||
gcePersistentDisk: GcePersistentDiskSource;
|
||||
gitRepo: GitRepoSource;
|
||||
glusterfs: GlusterFsSource;
|
||||
hostPath: HostPathSource;
|
||||
iscsi: IScsiSource;
|
||||
local: LocalSource;
|
||||
nfs: NetworkFsSource;
|
||||
persistentVolumeClaim: PersistentVolumeClaimSource;
|
||||
photonPersistentDisk: PhotonPersistentDiskSource;
|
||||
portworxVolume: PortworxVolumeSource;
|
||||
projected: ProjectedSource;
|
||||
quobyte: QuobyteSource;
|
||||
rbd: RadosBlockDeviceSource;
|
||||
scaleIO: ScaleIoSource;
|
||||
secret: SecretSource;
|
||||
storageos: StorageOsSource;
|
||||
vsphereVolume: VsphereVolumeSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* The valid kinds of volume
|
||||
*/
|
||||
export type PodVolumeKind = keyof PodVolumeVariants;
|
||||
|
||||
export type PodSpecVolume = RequireExactlyOne<PodVolumeVariants> & {
|
||||
name: string;
|
||||
};
|
||||
|
||||
export interface HostAlias {
|
||||
ip: string;
|
||||
hostnames: string[];
|
||||
}
|
||||
|
||||
export interface Sysctl {
|
||||
name: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface TopologySpreadConstraint {}
|
||||
|
||||
export interface PodSpec {
|
||||
activeDeadlineSeconds?: number;
|
||||
affinity?: Affinity;
|
||||
automountServiceAccountToken?: boolean;
|
||||
containers?: Container[];
|
||||
dnsPolicy?: string;
|
||||
enableServiceLinks?: boolean;
|
||||
ephemeralContainers?: unknown[];
|
||||
hostAliases?: HostAlias[];
|
||||
hostIPC?: boolean;
|
||||
hostname?: string;
|
||||
hostNetwork?: boolean;
|
||||
hostPID?: boolean;
|
||||
imagePullSecrets?: LocalObjectReference[];
|
||||
initContainers?: Container[];
|
||||
nodeName?: string;
|
||||
nodeSelector?: Partial<Record<string, string>>;
|
||||
overhead?: Partial<Record<string, string>>;
|
||||
preemptionPolicy?: string;
|
||||
priority?: number;
|
||||
priorityClassName?: string;
|
||||
readinessGates?: unknown[];
|
||||
restartPolicy?: string;
|
||||
runtimeClassName?: string;
|
||||
schedulerName?: string;
|
||||
securityContext?: PodSecurityContext;
|
||||
serviceAccount?: string;
|
||||
serviceAccountName?: string;
|
||||
setHostnameAsFQDN?: boolean;
|
||||
shareProcessNamespace?: boolean;
|
||||
subdomain?: string;
|
||||
terminationGracePeriodSeconds?: number;
|
||||
tolerations?: Toleration[];
|
||||
topologySpreadConstraints?: TopologySpreadConstraint[];
|
||||
volumes?: PodSpecVolume[];
|
||||
}
|
||||
|
||||
export interface PodCondition {
|
||||
lastProbeTime?: number;
|
||||
lastTransitionTime?: string;
|
||||
message?: string;
|
||||
reason?: string;
|
||||
type: string;
|
||||
status: string;
|
||||
}
|
||||
|
||||
export interface PodStatus {
|
||||
phase: string;
|
||||
conditions: PodCondition[];
|
||||
hostIP: string;
|
||||
podIP: string;
|
||||
podIPs?: {
|
||||
ip: string;
|
||||
}[];
|
||||
startTime: string;
|
||||
initContainerStatuses?: PodContainerStatus[];
|
||||
containerStatuses?: PodContainerStatus[];
|
||||
qosClass?: string;
|
||||
reason?: string;
|
||||
}
|
||||
|
||||
export class Pod extends KubeObject<NamespaceScopedMetadata, PodStatus, PodSpec> {
|
||||
static kind = "Pod";
|
||||
|
||||
static namespaced = true;
|
||||
|
||||
static apiBase = "/api/v1/pods";
|
||||
|
||||
getAffinityNumber() {
|
||||
return Object.keys(this.getAffinity()).length;
|
||||
}
|
||||
|
||||
getInitContainers() {
|
||||
return this.spec?.initContainers ?? [];
|
||||
}
|
||||
|
||||
getContainers() {
|
||||
return this.spec?.containers ?? [];
|
||||
}
|
||||
|
||||
getAllContainers() {
|
||||
return [...this.getContainers(), ...this.getInitContainers()];
|
||||
}
|
||||
|
||||
getRunningContainers() {
|
||||
const runningContainerNames = new Set(
|
||||
this.getContainerStatuses()
|
||||
.filter(({ state }) => state?.running)
|
||||
.map(({ name }) => name),
|
||||
);
|
||||
|
||||
return this.getAllContainers().filter(({ name }) => runningContainerNames.has(name));
|
||||
}
|
||||
|
||||
getContainerStatuses(includeInitContainers = true): PodContainerStatus[] {
|
||||
const { containerStatuses = [], initContainerStatuses = [] } = this.status ?? {};
|
||||
|
||||
if (includeInitContainers) {
|
||||
return [...containerStatuses, ...initContainerStatuses];
|
||||
}
|
||||
|
||||
return [...containerStatuses];
|
||||
}
|
||||
|
||||
getRestartsCount(): number {
|
||||
const { containerStatuses = [] } = this.status ?? {};
|
||||
|
||||
return containerStatuses.reduce((totalCount, { restartCount }) => totalCount + restartCount, 0);
|
||||
}
|
||||
|
||||
getQosClass() {
|
||||
return this.status?.qosClass || "";
|
||||
}
|
||||
|
||||
getReason() {
|
||||
return this.status?.reason || "";
|
||||
}
|
||||
|
||||
getPriorityClassName() {
|
||||
return this.spec?.priorityClassName || "";
|
||||
}
|
||||
|
||||
getRuntimeClassName() {
|
||||
return this.spec?.runtimeClassName || "";
|
||||
}
|
||||
|
||||
getServiceAccountName() {
|
||||
return this.spec?.serviceAccountName || "";
|
||||
}
|
||||
|
||||
getStatus(): PodStatusPhase {
|
||||
const phase = this.getStatusPhase();
|
||||
const reason = this.getReason();
|
||||
const trueConditionTypes = new Set(
|
||||
this.getConditions()
|
||||
.filter(({ status }) => status === "True")
|
||||
.map(({ type }) => type),
|
||||
);
|
||||
const isInGoodCondition = ["Initialized", "Ready"].every((condition) => trueConditionTypes.has(condition));
|
||||
|
||||
if (reason === PodStatusPhase.EVICTED) {
|
||||
return PodStatusPhase.EVICTED;
|
||||
}
|
||||
|
||||
if (phase === PodStatusPhase.FAILED) {
|
||||
return PodStatusPhase.FAILED;
|
||||
}
|
||||
|
||||
if (phase === PodStatusPhase.SUCCEEDED) {
|
||||
return PodStatusPhase.SUCCEEDED;
|
||||
}
|
||||
|
||||
if (phase === PodStatusPhase.RUNNING && isInGoodCondition) {
|
||||
return PodStatusPhase.RUNNING;
|
||||
}
|
||||
|
||||
return PodStatusPhase.PENDING;
|
||||
}
|
||||
|
||||
// Returns pod phase or container error if occurred
|
||||
getStatusMessage(): string {
|
||||
if (this.getReason() === PodStatusPhase.EVICTED) {
|
||||
return "Evicted";
|
||||
}
|
||||
|
||||
if (this.metadata.deletionTimestamp) {
|
||||
return "Terminating";
|
||||
}
|
||||
|
||||
return this.getStatusPhase() || "Waiting";
|
||||
}
|
||||
|
||||
getStatusPhase() {
|
||||
return this.status?.phase;
|
||||
}
|
||||
|
||||
getConditions() {
|
||||
return this.status?.conditions ?? [];
|
||||
}
|
||||
|
||||
getVolumes() {
|
||||
return this.spec?.volumes ?? [];
|
||||
}
|
||||
|
||||
getSecrets(): string[] {
|
||||
return this.getVolumes()
|
||||
.map((vol) => vol.secret?.secretName)
|
||||
.filter(isDefined);
|
||||
}
|
||||
|
||||
getNodeSelectors(): string[] {
|
||||
return Object.entries(this.spec?.nodeSelector ?? {}).map((values) => values.join(": "));
|
||||
}
|
||||
|
||||
getTolerations() {
|
||||
return this.spec?.tolerations ?? [];
|
||||
}
|
||||
|
||||
getAffinity(): Affinity {
|
||||
return this.spec?.affinity ?? {};
|
||||
}
|
||||
|
||||
hasIssues() {
|
||||
for (const { type, status } of this.getConditions()) {
|
||||
if (type === "Ready" && status !== "True") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (const { state } of this.getContainerStatuses()) {
|
||||
if (state?.waiting?.reason === "CrashLookBackOff") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return this.getStatusPhase() !== "Running";
|
||||
}
|
||||
|
||||
getLivenessProbe(container: Container) {
|
||||
return this.getProbe(container, container.livenessProbe);
|
||||
}
|
||||
|
||||
getReadinessProbe(container: Container) {
|
||||
return this.getProbe(container, container.readinessProbe);
|
||||
}
|
||||
|
||||
getStartupProbe(container: Container) {
|
||||
return this.getProbe(container, container.startupProbe);
|
||||
}
|
||||
|
||||
private getProbe(container: Container, probe: Probe | undefined): string[] {
|
||||
const probeItems: string[] = [];
|
||||
|
||||
if (!probe) {
|
||||
return probeItems;
|
||||
}
|
||||
|
||||
const {
|
||||
httpGet,
|
||||
exec,
|
||||
tcpSocket,
|
||||
initialDelaySeconds = 0,
|
||||
timeoutSeconds = 0,
|
||||
periodSeconds = 0,
|
||||
successThreshold = 0,
|
||||
failureThreshold = 0,
|
||||
} = probe;
|
||||
|
||||
// HTTP Request
|
||||
if (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;
|
||||
|
||||
probeItems.push("http-get", `${scheme.toLowerCase()}://${host}:${resolvedPort}${path}`);
|
||||
}
|
||||
|
||||
// Command
|
||||
if (exec?.command) {
|
||||
probeItems.push(`exec [${exec.command.join(" ")}]`);
|
||||
}
|
||||
|
||||
// TCP Probe
|
||||
if (tcpSocket?.port) {
|
||||
probeItems.push(`tcp-socket :${tcpSocket.port}`);
|
||||
}
|
||||
|
||||
probeItems.push(
|
||||
`delay=${initialDelaySeconds}s`,
|
||||
`timeout=${timeoutSeconds}s`,
|
||||
`period=${periodSeconds}s`,
|
||||
`#success=${successThreshold}`,
|
||||
`#failure=${failureThreshold}`,
|
||||
);
|
||||
|
||||
return probeItems;
|
||||
}
|
||||
|
||||
getNodeName(): string | undefined {
|
||||
return this.spec?.nodeName;
|
||||
}
|
||||
|
||||
getSelectedNodeOs(): string | undefined {
|
||||
return this.spec?.nodeSelector?.["kubernetes.io/os"] || this.spec?.nodeSelector?.["beta.kubernetes.io/os"];
|
||||
}
|
||||
|
||||
getIPs(): string[] {
|
||||
const podIPs = this.status?.podIPs ?? [];
|
||||
|
||||
return podIPs.map((value) => value.ip);
|
||||
}
|
||||
}
|
||||
@@ -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 { KubeJsonApiData, KubeObjectMetadata, KubeObjectScope, ClusterScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import type { PreemptionPolicy } from "../types/preemption-policy";
|
||||
|
||||
export interface PriorityClassData extends KubeJsonApiData<KubeObjectMetadata<KubeObjectScope.Cluster>, void, void> {
|
||||
description?: string;
|
||||
globalDefault?: boolean;
|
||||
preemptionPolicy?: PreemptionPolicy;
|
||||
value: number;
|
||||
}
|
||||
|
||||
export class PriorityClass extends KubeObject<ClusterScopedMetadata, void, void> {
|
||||
static readonly kind = "PriorityClass";
|
||||
|
||||
static readonly namespaced = false;
|
||||
|
||||
static readonly apiBase = "/apis/scheduling.k8s.io/v1/priorityclasses";
|
||||
|
||||
description?: string;
|
||||
|
||||
globalDefault?: boolean;
|
||||
|
||||
preemptionPolicy?: PreemptionPolicy;
|
||||
|
||||
value?: number;
|
||||
|
||||
constructor({ description, globalDefault, preemptionPolicy, value, ...rest }: PriorityClassData) {
|
||||
super(rest);
|
||||
this.description = description;
|
||||
this.globalDefault = globalDefault;
|
||||
this.preemptionPolicy = preemptionPolicy;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
getDescription() {
|
||||
return this.description || "";
|
||||
}
|
||||
|
||||
getGlobalDefault() {
|
||||
return (this.globalDefault || false).toString();
|
||||
}
|
||||
|
||||
getPreemptionPolicy() {
|
||||
return this.preemptionPolicy || "PreemptLowerPriority";
|
||||
}
|
||||
|
||||
getValue() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { LabelSelector, KubeObjectStatus, NamespaceScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import type { PodTemplateSpec } from "../types/pod-template-spec";
|
||||
|
||||
export interface ReplicaSetSpec {
|
||||
replicas?: number;
|
||||
selector: LabelSelector;
|
||||
template?: PodTemplateSpec;
|
||||
minReadySeconds?: number;
|
||||
}
|
||||
|
||||
export interface ReplicaSetStatus extends KubeObjectStatus {
|
||||
replicas: number;
|
||||
fullyLabeledReplicas?: number;
|
||||
readyReplicas?: number;
|
||||
availableReplicas?: number;
|
||||
observedGeneration?: number;
|
||||
}
|
||||
|
||||
export class ReplicaSet extends KubeObject<NamespaceScopedMetadata, ReplicaSetStatus, ReplicaSetSpec> {
|
||||
static kind = "ReplicaSet";
|
||||
|
||||
static namespaced = true;
|
||||
|
||||
static apiBase = "/apis/apps/v1/replicasets";
|
||||
|
||||
getSelectors(): string[] {
|
||||
return KubeObject.stringifyLabels(this.spec.selector.matchLabels);
|
||||
}
|
||||
|
||||
getNodeSelectors(): string[] {
|
||||
return KubeObject.stringifyLabels(this.spec.template?.spec?.nodeSelector);
|
||||
}
|
||||
|
||||
getTemplateLabels(): string[] {
|
||||
return KubeObject.stringifyLabels(this.spec.template?.metadata?.labels);
|
||||
}
|
||||
|
||||
getTolerations() {
|
||||
return this.spec.template?.spec?.tolerations ?? [];
|
||||
}
|
||||
|
||||
getAffinity() {
|
||||
return this.spec.template?.spec?.affinity;
|
||||
}
|
||||
|
||||
getAffinityNumber() {
|
||||
return Object.keys(this.getAffinity() ?? {}).length;
|
||||
}
|
||||
|
||||
getDesired() {
|
||||
return this.spec.replicas ?? 0;
|
||||
}
|
||||
|
||||
getCurrent() {
|
||||
return this.status?.availableReplicas ?? 0;
|
||||
}
|
||||
|
||||
getReady() {
|
||||
return this.status?.readyReplicas ?? 0;
|
||||
}
|
||||
|
||||
getImages() {
|
||||
const containers = this.spec.template?.spec?.containers ?? [];
|
||||
|
||||
return containers.map((container) => container.image);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type {
|
||||
KubeObjectMetadata,
|
||||
KubeObjectStatus,
|
||||
NamespaceScopedMetadata,
|
||||
BaseKubeObjectCondition,
|
||||
} from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import type { PodTemplateSpec } from "../types/pod-template-spec";
|
||||
|
||||
export interface Scale {
|
||||
apiVersion: "autoscaling/v1";
|
||||
kind: "Scale";
|
||||
metadata: KubeObjectMetadata;
|
||||
spec: {
|
||||
replicas: number;
|
||||
};
|
||||
status: {
|
||||
replicas: number;
|
||||
selector: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ReplicationControllerSpec {
|
||||
/**
|
||||
* Minimum number of seconds for which a newly created pod should be ready without any of its container crashing,
|
||||
* for it to be considered available.
|
||||
* Defaults to 0 (pod will be considered available as soon as it is ready)
|
||||
*/
|
||||
minReadySeconds?: number;
|
||||
/**
|
||||
* Replicas is the number of desired replicas. This is a pointer to distinguish between explicit zero and unspecified.
|
||||
* Defaults to 1. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#what-is-a-replicationcontroller
|
||||
*/
|
||||
replicas?: number;
|
||||
/**
|
||||
* Selector is a label query over pods that should match the Replicas count.
|
||||
* If Selector is empty, it is defaulted to the labels present on the Pod template.
|
||||
* Label keys and values that must match in order to be controlled by this replication controller,
|
||||
* if empty defaulted to labels on Pod template.
|
||||
* More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors
|
||||
*/
|
||||
selector?: Record<string, string>;
|
||||
/**
|
||||
* Template is the object that describes the pod that will be created if insufficient replicas are detected.
|
||||
* This takes precedence over a TemplateRef.
|
||||
* More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#pod-template
|
||||
*/
|
||||
template: PodTemplateSpec;
|
||||
}
|
||||
|
||||
export interface ReplicationControllerStatus extends KubeObjectStatus {
|
||||
/**
|
||||
* The number of available replicas (ready for at least minReadySeconds) for this replication controller.
|
||||
*/
|
||||
availableReplicas: number;
|
||||
/**
|
||||
* The number of pods that have labels matching the labels of the pod template of the replication controller.
|
||||
*/
|
||||
fullyLabeledReplicas: number;
|
||||
/**
|
||||
* ObservedGeneration reflects the generation of the most recently observed replication controller.
|
||||
*/
|
||||
observedGeneration: number;
|
||||
/**
|
||||
* The number of ready replicas for this replication controller.
|
||||
*/
|
||||
readyReplicas: number;
|
||||
/**
|
||||
* Replicas is the most recently observed number of replicas.
|
||||
* More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#what-is-a-replicationcontroller
|
||||
*/
|
||||
replicas: number;
|
||||
}
|
||||
|
||||
export class ReplicationController extends KubeObject<
|
||||
NamespaceScopedMetadata,
|
||||
ReplicationControllerStatus,
|
||||
ReplicationControllerSpec
|
||||
> {
|
||||
static kind = "ReplicationController";
|
||||
|
||||
static namespaced = true;
|
||||
|
||||
static apiBase = "/api/v1/replicationcontrollers";
|
||||
|
||||
getMinReadySeconds(): number {
|
||||
return this.spec?.minReadySeconds ?? 0;
|
||||
}
|
||||
|
||||
getGeneration() {
|
||||
return this.status?.observedGeneration;
|
||||
}
|
||||
|
||||
getSelectorLabels(): string[] {
|
||||
return KubeObject.stringifyLabels(this.spec.selector);
|
||||
}
|
||||
|
||||
getReplicas(): number | undefined {
|
||||
return this.status?.replicas;
|
||||
}
|
||||
|
||||
getDesiredReplicas(): number {
|
||||
return this.spec?.replicas ?? 0;
|
||||
}
|
||||
|
||||
getAvailableReplicas(): number | undefined {
|
||||
return this.status?.availableReplicas;
|
||||
}
|
||||
|
||||
getLabeledReplicas(): number | undefined {
|
||||
return this.status?.fullyLabeledReplicas;
|
||||
}
|
||||
|
||||
getConditions(): BaseKubeObjectCondition[] {
|
||||
return this.status?.conditions ?? [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { NamespaceScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
|
||||
export type ResourceQuotaValues = Partial<Record<string, string>> & {
|
||||
// Compute Resource Quota
|
||||
"limits.cpu"?: string;
|
||||
"limits.memory"?: string;
|
||||
"requests.cpu"?: string;
|
||||
"requests.memory"?: string;
|
||||
|
||||
// Storage Resource Quota
|
||||
"requests.storage"?: string;
|
||||
persistentvolumeclaims?: string;
|
||||
|
||||
// Object Count Quota
|
||||
"count/pods"?: string;
|
||||
"count/persistentvolumeclaims"?: string;
|
||||
"count/services"?: string;
|
||||
"count/secrets"?: string;
|
||||
"count/configmaps"?: string;
|
||||
"count/replicationcontrollers"?: string;
|
||||
"count/deployments.apps"?: string;
|
||||
"count/replicasets.apps"?: string;
|
||||
"count/statefulsets.apps"?: string;
|
||||
"count/jobs.batch"?: string;
|
||||
"count/cronjobs.batch"?: string;
|
||||
"count/deployments.extensions"?: string;
|
||||
};
|
||||
|
||||
export interface ResourceQuotaSpec {
|
||||
hard: ResourceQuotaValues;
|
||||
scopeSelector?: {
|
||||
matchExpressions: {
|
||||
operator: string;
|
||||
scopeName: string;
|
||||
values: string[];
|
||||
}[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface ResourceQuotaStatus {
|
||||
hard: ResourceQuotaValues;
|
||||
used: ResourceQuotaValues;
|
||||
}
|
||||
|
||||
export class ResourceQuota extends KubeObject<NamespaceScopedMetadata, ResourceQuotaStatus, ResourceQuotaSpec> {
|
||||
static readonly kind = "ResourceQuota";
|
||||
|
||||
static readonly namespaced = true;
|
||||
|
||||
static readonly apiBase = "/api/v1/resourcequotas";
|
||||
|
||||
getScopeSelector() {
|
||||
return this.spec.scopeSelector?.matchExpressions ?? [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { KubeJsonApiData, KubeObjectMetadata, KubeObjectScope, NamespaceScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import type { RoleRef } from "../types/role-ref";
|
||||
import type { Subject } from "../types/subject";
|
||||
|
||||
export interface RoleBindingData extends KubeJsonApiData<KubeObjectMetadata<KubeObjectScope.Namespace>, void, void> {
|
||||
subjects?: Subject[];
|
||||
roleRef: RoleRef;
|
||||
}
|
||||
|
||||
export class RoleBinding extends KubeObject<NamespaceScopedMetadata, void, void> {
|
||||
static readonly kind = "RoleBinding";
|
||||
|
||||
static readonly namespaced = true;
|
||||
|
||||
static readonly apiBase = "/apis/rbac.authorization.k8s.io/v1/rolebindings";
|
||||
|
||||
subjects?: Subject[];
|
||||
|
||||
roleRef: RoleRef;
|
||||
|
||||
constructor({ subjects, roleRef, ...rest }: RoleBindingData) {
|
||||
super(rest);
|
||||
this.subjects = subjects;
|
||||
this.roleRef = roleRef;
|
||||
}
|
||||
|
||||
getSubjects() {
|
||||
return this.subjects || [];
|
||||
}
|
||||
|
||||
getSubjectNames(): string {
|
||||
return this.getSubjects()
|
||||
.map((subject) => subject.name)
|
||||
.join(", ");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { KubeJsonApiData, KubeObjectMetadata, KubeObjectScope, NamespaceScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import type { PolicyRule } from "../types/policy-rule";
|
||||
|
||||
export interface RoleData extends KubeJsonApiData<KubeObjectMetadata<KubeObjectScope.Namespace>, void, void> {
|
||||
rules?: PolicyRule[];
|
||||
}
|
||||
|
||||
export class Role extends KubeObject<NamespaceScopedMetadata, void, void> {
|
||||
static readonly kind = "Role";
|
||||
|
||||
static readonly namespaced = true;
|
||||
|
||||
static readonly apiBase = "/apis/rbac.authorization.k8s.io/v1/roles";
|
||||
|
||||
rules?: PolicyRule[];
|
||||
|
||||
constructor({ rules, ...rest }: RoleData) {
|
||||
super(rest);
|
||||
this.rules = rules;
|
||||
}
|
||||
|
||||
getRules() {
|
||||
return this.rules || [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type {
|
||||
KubeJsonApiData,
|
||||
KubeObjectMetadata,
|
||||
KubeObjectScope,
|
||||
Toleration,
|
||||
ClusterScopedMetadata,
|
||||
} from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
|
||||
export interface RuntimeClassData extends KubeJsonApiData<KubeObjectMetadata<KubeObjectScope.Cluster>, void, void> {
|
||||
handler: string;
|
||||
overhead?: RuntimeClassOverhead;
|
||||
scheduling?: RuntimeClassScheduling;
|
||||
}
|
||||
|
||||
export interface RuntimeClassOverhead {
|
||||
podFixed?: string;
|
||||
}
|
||||
|
||||
export interface RuntimeClassScheduling {
|
||||
nodeSelector?: Partial<Record<string, string>>;
|
||||
tolerations?: Toleration[];
|
||||
}
|
||||
|
||||
export class RuntimeClass extends KubeObject<ClusterScopedMetadata, void, void> {
|
||||
static readonly kind = "RuntimeClass";
|
||||
|
||||
static readonly namespaced = false;
|
||||
|
||||
static readonly apiBase = "/apis/node.k8s.io/v1/runtimeclasses";
|
||||
|
||||
handler: string;
|
||||
|
||||
overhead?: RuntimeClassOverhead;
|
||||
|
||||
scheduling?: RuntimeClassScheduling;
|
||||
|
||||
constructor({ handler, overhead, scheduling, ...rest }: RuntimeClassData) {
|
||||
super(rest);
|
||||
this.handler = handler;
|
||||
this.overhead = overhead;
|
||||
this.scheduling = scheduling;
|
||||
}
|
||||
|
||||
getHandler() {
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
getPodFixed() {
|
||||
return this.overhead?.podFixed ?? "";
|
||||
}
|
||||
|
||||
getNodeSelectors(): string[] {
|
||||
return Object.entries(this.scheduling?.nodeSelector ?? {}).map((values) => values.join(": "));
|
||||
}
|
||||
|
||||
getTolerations() {
|
||||
return this.scheduling?.tolerations ?? [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import autoBind from "auto-bind";
|
||||
import type { KubeJsonApiData, KubeObjectMetadata, KubeObjectScope, NamespaceScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
|
||||
export enum SecretType {
|
||||
Opaque = "Opaque",
|
||||
ServiceAccountToken = "kubernetes.io/service-account-token",
|
||||
Dockercfg = "kubernetes.io/dockercfg",
|
||||
DockerConfigJson = "kubernetes.io/dockerconfigjson",
|
||||
BasicAuth = "kubernetes.io/basic-auth",
|
||||
SSHAuth = "kubernetes.io/ssh-auth",
|
||||
TLS = "kubernetes.io/tls",
|
||||
BootstrapToken = "bootstrap.kubernetes.io/token",
|
||||
}
|
||||
|
||||
export const reverseSecretTypeMap = {
|
||||
[SecretType.Opaque]: "Opaque",
|
||||
[SecretType.ServiceAccountToken]: "ServiceAccountToken",
|
||||
[SecretType.Dockercfg]: "Dockercfg",
|
||||
[SecretType.DockerConfigJson]: "DockerConfigJson",
|
||||
[SecretType.BasicAuth]: "BasicAuth",
|
||||
[SecretType.SSHAuth]: "SSHAuth",
|
||||
[SecretType.TLS]: "TLS",
|
||||
[SecretType.BootstrapToken]: "BootstrapToken",
|
||||
};
|
||||
|
||||
export interface SecretReference {
|
||||
name: string;
|
||||
namespace?: string;
|
||||
}
|
||||
|
||||
export interface SecretData extends KubeJsonApiData<KubeObjectMetadata<KubeObjectScope.Namespace>, void, void> {
|
||||
type: SecretType;
|
||||
data?: Partial<Record<string, string>>;
|
||||
}
|
||||
|
||||
export class Secret extends KubeObject<NamespaceScopedMetadata, void, void> {
|
||||
static readonly kind = "Secret";
|
||||
|
||||
static readonly namespaced = true;
|
||||
|
||||
static readonly apiBase = "/api/v1/secrets";
|
||||
|
||||
type: SecretType;
|
||||
|
||||
data: Partial<Record<string, string>>;
|
||||
|
||||
constructor({ data = {}, type, ...rest }: SecretData) {
|
||||
super(rest);
|
||||
autoBind(this);
|
||||
|
||||
this.data = data;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
getKeys(): string[] {
|
||||
return Object.keys(this.data);
|
||||
}
|
||||
|
||||
getToken() {
|
||||
return this.data.token;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { ClusterScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
|
||||
export interface SelfSubjectReviewRule {
|
||||
verbs: string[];
|
||||
apiGroups?: string[];
|
||||
resources?: string[];
|
||||
resourceNames?: string[];
|
||||
nonResourceURLs?: string[];
|
||||
}
|
||||
|
||||
export interface SelfSubjectRulesReviewSpec {
|
||||
namespace?: string;
|
||||
}
|
||||
|
||||
export interface SelfSubjectRulesReviewStatus {
|
||||
resourceRules: SelfSubjectReviewRule[];
|
||||
nonResourceRules: SelfSubjectReviewRule[];
|
||||
incomplete: boolean;
|
||||
}
|
||||
|
||||
export class SelfSubjectRulesReview extends KubeObject<
|
||||
ClusterScopedMetadata,
|
||||
SelfSubjectRulesReviewStatus,
|
||||
SelfSubjectRulesReviewSpec
|
||||
> {
|
||||
static kind = "SelfSubjectRulesReview";
|
||||
|
||||
static namespaced = false;
|
||||
|
||||
static apiBase = "/apis/authorization.k8s.io/v1/selfsubjectrulesreviews";
|
||||
|
||||
getResourceRules() {
|
||||
const rules = (this.status && this.status.resourceRules) || [];
|
||||
|
||||
return rules.map((rule) => this.normalize(rule));
|
||||
}
|
||||
|
||||
getNonResourceRules() {
|
||||
const rules = (this.status && this.status.nonResourceRules) || [];
|
||||
|
||||
return rules.map((rule) => this.normalize(rule));
|
||||
}
|
||||
|
||||
protected normalize(rule: SelfSubjectReviewRule): SelfSubjectReviewRule {
|
||||
const { apiGroups = [], resourceNames = [], verbs = [], nonResourceURLs = [], resources = [] } = rule;
|
||||
|
||||
return {
|
||||
apiGroups,
|
||||
nonResourceURLs,
|
||||
resourceNames,
|
||||
verbs,
|
||||
resources: resources.map((resource, index) => {
|
||||
const apiGroup = apiGroups.length >= index + 1 ? apiGroups[index] : apiGroups.slice(-1)[0];
|
||||
const separator = apiGroup === "" ? "" : ".";
|
||||
|
||||
return resource + separator + apiGroup;
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type {
|
||||
KubeJsonApiData,
|
||||
KubeObjectMetadata,
|
||||
KubeObjectScope,
|
||||
LocalObjectReference,
|
||||
ObjectReference,
|
||||
NamespaceScopedMetadata,
|
||||
} from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
|
||||
export interface ServiceAccountData extends KubeJsonApiData<KubeObjectMetadata<KubeObjectScope.Namespace>, void, void> {
|
||||
automountServiceAccountToken?: boolean;
|
||||
imagePullSecrets?: LocalObjectReference[];
|
||||
secrets?: ObjectReference[];
|
||||
}
|
||||
|
||||
export class ServiceAccount extends KubeObject<NamespaceScopedMetadata, void, void> {
|
||||
static readonly kind = "ServiceAccount";
|
||||
|
||||
static readonly namespaced = true;
|
||||
|
||||
static readonly apiBase = "/api/v1/serviceaccounts";
|
||||
|
||||
automountServiceAccountToken?: boolean;
|
||||
|
||||
imagePullSecrets?: LocalObjectReference[];
|
||||
|
||||
secrets?: ObjectReference[];
|
||||
|
||||
constructor({ automountServiceAccountToken, imagePullSecrets, secrets, ...rest }: ServiceAccountData) {
|
||||
super(rest);
|
||||
this.automountServiceAccountToken = automountServiceAccountToken;
|
||||
this.imagePullSecrets = imagePullSecrets;
|
||||
this.secrets = secrets;
|
||||
}
|
||||
|
||||
getSecrets() {
|
||||
return this.secrets || [];
|
||||
}
|
||||
|
||||
getImagePullSecrets() {
|
||||
return this.imagePullSecrets || [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { NamespaceScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
|
||||
export interface ServicePortSpec {
|
||||
name?: string;
|
||||
protocol: string;
|
||||
port: number;
|
||||
targetPort: number;
|
||||
nodePort?: number;
|
||||
}
|
||||
|
||||
export class ServicePort {
|
||||
name?: string;
|
||||
|
||||
protocol: string;
|
||||
|
||||
port: number;
|
||||
|
||||
targetPort: number;
|
||||
|
||||
nodePort?: number;
|
||||
|
||||
constructor(data: ServicePortSpec) {
|
||||
this.name = data.name;
|
||||
this.protocol = data.protocol;
|
||||
this.port = data.port;
|
||||
this.targetPort = data.targetPort;
|
||||
this.nodePort = data.nodePort;
|
||||
}
|
||||
|
||||
toString() {
|
||||
const targetPort = this.nodePort ? `:${this.nodePort}` : this.port !== this.targetPort ? `:${this.targetPort}` : "";
|
||||
|
||||
return `${this.port}${targetPort}/${this.protocol}`;
|
||||
}
|
||||
}
|
||||
|
||||
export interface ServiceSpec {
|
||||
type: string;
|
||||
clusterIP: string;
|
||||
clusterIPs?: string[];
|
||||
externalTrafficPolicy?: string;
|
||||
externalName?: string;
|
||||
loadBalancerIP?: string;
|
||||
loadBalancerSourceRanges?: string[];
|
||||
sessionAffinity: string;
|
||||
selector: Partial<Record<string, string>>;
|
||||
ports: ServicePortSpec[];
|
||||
healthCheckNodePort?: number;
|
||||
externalIPs?: string[]; // https://kubernetes.io/docs/concepts/services-networking/service/#external-ips
|
||||
topologyKeys?: string[];
|
||||
ipFamilies?: string[];
|
||||
ipFamilyPolicy?: string;
|
||||
allocateLoadBalancerNodePorts?: boolean;
|
||||
loadBalancerClass?: string;
|
||||
internalTrafficPolicy?: string;
|
||||
}
|
||||
|
||||
export interface ServiceStatus {
|
||||
loadBalancer?: {
|
||||
ingress?: {
|
||||
ip?: string;
|
||||
hostname?: string;
|
||||
}[];
|
||||
};
|
||||
}
|
||||
|
||||
export class Service extends KubeObject<NamespaceScopedMetadata, ServiceStatus, ServiceSpec> {
|
||||
static readonly kind = "Service";
|
||||
|
||||
static readonly namespaced = true;
|
||||
|
||||
static readonly apiBase = "/api/v1/services";
|
||||
|
||||
getClusterIp() {
|
||||
return this.spec.clusterIP;
|
||||
}
|
||||
|
||||
getClusterIps() {
|
||||
return this.spec.clusterIPs || [];
|
||||
}
|
||||
|
||||
getExternalIps() {
|
||||
const lb = this.getLoadBalancer();
|
||||
|
||||
if (lb?.ingress) {
|
||||
return lb.ingress.map((val) => val.ip || val.hostname);
|
||||
}
|
||||
|
||||
if (Array.isArray(this.spec?.externalIPs)) {
|
||||
return this.spec.externalIPs;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
getType() {
|
||||
return this.spec.type || "-";
|
||||
}
|
||||
|
||||
getSelector(): string[] {
|
||||
if (!this.spec.selector) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Object.entries(this.spec.selector).map((val) => val.join("="));
|
||||
}
|
||||
|
||||
getPorts(): ServicePort[] {
|
||||
const ports = this.spec.ports || [];
|
||||
|
||||
return ports.map((p) => new ServicePort(p));
|
||||
}
|
||||
|
||||
getLoadBalancer() {
|
||||
return this.status?.loadBalancer;
|
||||
}
|
||||
|
||||
isActive() {
|
||||
return this.getType() !== "LoadBalancer" || this.getExternalIps().length > 0;
|
||||
}
|
||||
|
||||
getStatus() {
|
||||
return this.isActive() ? "Active" : "Pending";
|
||||
}
|
||||
|
||||
getIpFamilies() {
|
||||
return this.spec.ipFamilies || [];
|
||||
}
|
||||
|
||||
getIpFamilyPolicy() {
|
||||
return this.spec.ipFamilyPolicy || "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { LabelSelector, NamespaceScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import type { PersistentVolumeClaimTemplateSpec } from "../types/persistent-volume-claim-template-spec";
|
||||
import type { PodTemplateSpec } from "../types/pod-template-spec";
|
||||
|
||||
export interface StatefulSetSpec {
|
||||
serviceName: string;
|
||||
replicas: number;
|
||||
selector: LabelSelector;
|
||||
template: PodTemplateSpec;
|
||||
volumeClaimTemplates: PersistentVolumeClaimTemplateSpec[];
|
||||
}
|
||||
|
||||
export interface StatefulSetStatus {
|
||||
observedGeneration: number;
|
||||
replicas: number;
|
||||
currentReplicas: number;
|
||||
readyReplicas: number;
|
||||
currentRevision: string;
|
||||
updateRevision: string;
|
||||
collisionCount: number;
|
||||
}
|
||||
|
||||
export class StatefulSet extends KubeObject<NamespaceScopedMetadata, StatefulSetStatus, StatefulSetSpec> {
|
||||
static readonly kind = "StatefulSet";
|
||||
|
||||
static readonly namespaced = true;
|
||||
|
||||
static readonly apiBase = "/apis/apps/v1/statefulsets";
|
||||
|
||||
getSelectors(): string[] {
|
||||
return KubeObject.stringifyLabels(this.spec.selector.matchLabels);
|
||||
}
|
||||
|
||||
getNodeSelectors(): string[] {
|
||||
return KubeObject.stringifyLabels(this.spec.template.spec?.nodeSelector);
|
||||
}
|
||||
|
||||
getTemplateLabels(): string[] {
|
||||
return KubeObject.stringifyLabels(this.spec.template.metadata?.labels);
|
||||
}
|
||||
|
||||
getTolerations() {
|
||||
return this.spec.template.spec?.tolerations ?? [];
|
||||
}
|
||||
|
||||
getAffinity() {
|
||||
return this.spec.template.spec?.affinity ?? {};
|
||||
}
|
||||
|
||||
getAffinityNumber() {
|
||||
return Object.keys(this.getAffinity()).length;
|
||||
}
|
||||
|
||||
getReplicas() {
|
||||
return this.spec.replicas || 0;
|
||||
}
|
||||
|
||||
getImages() {
|
||||
const containers = this.spec.template?.spec?.containers ?? [];
|
||||
|
||||
return containers.map((container) => container.image);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import autoBind from "auto-bind";
|
||||
import type { KubeJsonApiData, KubeObjectMetadata, KubeObjectScope, ClusterScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
|
||||
export interface TopologySelectorLabelRequirement {
|
||||
key: string;
|
||||
values: string[];
|
||||
}
|
||||
|
||||
export interface TopologySelectorTerm {
|
||||
matchLabelExpressions?: TopologySelectorLabelRequirement[];
|
||||
}
|
||||
|
||||
export interface StorageClassData extends KubeJsonApiData<KubeObjectMetadata<KubeObjectScope.Cluster>, void, void> {
|
||||
allowVolumeExpansion?: boolean;
|
||||
allowedTopologies?: TopologySelectorTerm[];
|
||||
mountOptions?: string[];
|
||||
parameters?: Partial<Record<string, string>>;
|
||||
provisioner: string;
|
||||
reclaimPolicy?: string;
|
||||
volumeBindingMode?: string;
|
||||
}
|
||||
|
||||
export class StorageClass extends KubeObject<ClusterScopedMetadata, void, void> {
|
||||
static readonly kind = "StorageClass";
|
||||
|
||||
static readonly namespaced = false;
|
||||
|
||||
static readonly apiBase = "/apis/storage.k8s.io/v1/storageclasses";
|
||||
|
||||
allowVolumeExpansion?: boolean;
|
||||
|
||||
allowedTopologies: TopologySelectorTerm[];
|
||||
|
||||
mountOptions: string[];
|
||||
|
||||
parameters: Partial<Record<string, string>>;
|
||||
|
||||
provisioner: string;
|
||||
|
||||
reclaimPolicy: string;
|
||||
|
||||
volumeBindingMode?: string;
|
||||
|
||||
constructor({
|
||||
allowVolumeExpansion,
|
||||
allowedTopologies = [],
|
||||
mountOptions = [],
|
||||
parameters = {},
|
||||
provisioner,
|
||||
reclaimPolicy = "Delete",
|
||||
volumeBindingMode,
|
||||
...rest
|
||||
}: StorageClassData) {
|
||||
super(rest);
|
||||
autoBind(this);
|
||||
this.allowVolumeExpansion = allowVolumeExpansion;
|
||||
this.allowedTopologies = allowedTopologies;
|
||||
this.mountOptions = mountOptions;
|
||||
this.parameters = parameters;
|
||||
this.provisioner = provisioner;
|
||||
this.reclaimPolicy = reclaimPolicy;
|
||||
this.volumeBindingMode = volumeBindingMode;
|
||||
}
|
||||
|
||||
isDefault() {
|
||||
const annotations = this.metadata.annotations || {};
|
||||
|
||||
return (
|
||||
annotations["storageclass.kubernetes.io/is-default-class"] === "true" ||
|
||||
annotations["storageclass.beta.kubernetes.io/is-default-class"] === "true"
|
||||
);
|
||||
}
|
||||
|
||||
getVolumeBindingMode() {
|
||||
return this.volumeBindingMode || "-";
|
||||
}
|
||||
|
||||
getReclaimPolicy() {
|
||||
return this.reclaimPolicy || "-";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { KubeJsonApiData, KubeObjectMetadata, KubeObjectScope, NamespaceScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import type { Webhook } from "./mutating-webhook-configuration";
|
||||
|
||||
export interface ValidatingWebhookConfigurationData
|
||||
extends KubeJsonApiData<KubeObjectMetadata<KubeObjectScope.Namespace>, void, void> {
|
||||
webhooks?: Webhook[];
|
||||
}
|
||||
|
||||
export class ValidatingWebhookConfiguration extends KubeObject<NamespaceScopedMetadata, void, void> {
|
||||
static kind = "ValidatingWebhookConfiguration";
|
||||
|
||||
static namespaced = false;
|
||||
|
||||
static apiBase = "/apis/admissionregistration.k8s.io/v1/validatingwebhookconfigurations";
|
||||
|
||||
webhooks?: Webhook[];
|
||||
|
||||
constructor({ webhooks, ...rest }: ValidatingWebhookConfigurationData) {
|
||||
super(rest);
|
||||
this.webhooks = webhooks;
|
||||
}
|
||||
|
||||
getWebhooks(): Webhook[] {
|
||||
return this.webhooks ?? [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { BaseKubeObjectCondition, NamespaceScopedMetadata } from "../api-types";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import type { CrossVersionObjectReference } from "../types/cross-version-object-reference";
|
||||
|
||||
export enum ResourceName {
|
||||
ResourceCPU = "cpu",
|
||||
ResourceMemory = "memory",
|
||||
ResourceStorage = "storage",
|
||||
}
|
||||
|
||||
export type ResourceList = Partial<Record<string, string>>;
|
||||
|
||||
export interface RecommendedContainerResources {
|
||||
containerName?: string;
|
||||
target: ResourceList;
|
||||
lowerBound?: ResourceList;
|
||||
upperBound?: ResourceList;
|
||||
uncappedTarget?: ResourceList;
|
||||
}
|
||||
export interface RecommendedPodResources {
|
||||
containerRecommendations?: RecommendedContainerResources[];
|
||||
}
|
||||
|
||||
export interface VerticalPodAutoscalerStatus {
|
||||
conditions?: BaseKubeObjectCondition[];
|
||||
recommendation?: RecommendedPodResources;
|
||||
}
|
||||
|
||||
export interface VerticalPodAutoscalerRecommenderSelector {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export enum ContainerScalingMode {
|
||||
ContainerScalingModeAuto = "Auto",
|
||||
ContainerScalingModeOff = "Off",
|
||||
}
|
||||
|
||||
export enum ControlledValues {
|
||||
ControlledValueRequestsAndLimits = "RequestsAndLimits",
|
||||
ControlledValueRequestsOnly = "RequestsOnly",
|
||||
}
|
||||
|
||||
/**
|
||||
* ContainerResourcePolicy controls how autoscaler computes the recommended resources for
|
||||
* a specific container.
|
||||
*/
|
||||
export interface ContainerResourcePolicy {
|
||||
containerName?: string;
|
||||
mode?: ContainerScalingMode;
|
||||
minAllowed?: ResourceList;
|
||||
maxAllowed?: ResourceList;
|
||||
controlledResources?: ResourceName[];
|
||||
controlledValues?: ControlledValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls how the autoscaler computes recommended resources.
|
||||
* The resource policy may be used to set constraints on the recommendations for individual
|
||||
* containers.
|
||||
* If not specified, the autoscaler computes recommended resources for all containers in the
|
||||
* pod, without additional constraints.
|
||||
*/
|
||||
export interface PodResourcePolicy {
|
||||
containerPolicies?: ContainerResourcePolicy[]; // Per-container resource policies.
|
||||
}
|
||||
|
||||
export enum UpdateMode {
|
||||
/**
|
||||
* UpdateModeOff means that autoscaler never changes Pod resources.
|
||||
* The recommender still sets the recommended resources in the
|
||||
* VerticalPodAutoscaler object. This can be used for a "dry run".
|
||||
*/
|
||||
UpdateModeOff = "Off",
|
||||
/**
|
||||
* UpdateModeInitial means that autoscaler only assigns resources on pod
|
||||
* creation and does not change them during the lifetime of the pod.
|
||||
*/
|
||||
UpdateModeInitial = "Initial",
|
||||
/**
|
||||
* UpdateModeRecreate means that autoscaler assigns resources on pod
|
||||
* creation and additionally can update them during the lifetime of the
|
||||
* pod by deleting and recreating the pod.
|
||||
*/
|
||||
UpdateModeRecreate = "Recreate",
|
||||
/**
|
||||
* UpdateModeAuto means that autoscaler assigns resources on pod creation
|
||||
* and additionally can update them during the lifetime of the pod,
|
||||
* using any available update method. Currently this is equivalent to
|
||||
* Recreate, which is the only available update method.
|
||||
*/
|
||||
UpdateModeAuto = "Auto",
|
||||
}
|
||||
export interface PodUpdatePolicy {
|
||||
minReplicas?: number;
|
||||
updateMode?: UpdateMode;
|
||||
}
|
||||
|
||||
export interface VerticalPodAutoscalerSpec {
|
||||
targetRef: CrossVersionObjectReference;
|
||||
updatePolicy?: PodUpdatePolicy;
|
||||
resourcePolicy?: PodResourcePolicy;
|
||||
recommenders?: VerticalPodAutoscalerRecommenderSelector[];
|
||||
}
|
||||
|
||||
export class VerticalPodAutoscaler extends KubeObject<
|
||||
NamespaceScopedMetadata,
|
||||
VerticalPodAutoscalerStatus,
|
||||
VerticalPodAutoscalerSpec
|
||||
> {
|
||||
static readonly kind = "VerticalPodAutoscaler";
|
||||
|
||||
static readonly namespaced = true;
|
||||
|
||||
static readonly apiBase = "/apis/autoscaling.k8s.io/v1/verticalpodautoscalers";
|
||||
|
||||
getReadyConditions() {
|
||||
return this.getConditions().filter(({ isReady }) => isReady);
|
||||
}
|
||||
|
||||
getConditions() {
|
||||
return (
|
||||
this.status?.conditions?.map((condition) => {
|
||||
const { message, reason, lastTransitionTime, status } = condition;
|
||||
|
||||
return {
|
||||
...condition,
|
||||
isReady: status === "True",
|
||||
tooltip: `${message || reason || ""} (${lastTransitionTime})`,
|
||||
};
|
||||
}) ?? []
|
||||
);
|
||||
}
|
||||
|
||||
getMode() {
|
||||
return this.spec.updatePolicy?.updateMode ?? UpdateMode.UpdateModeAuto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { LabelSelector } from "../api-types";
|
||||
|
||||
export interface AggregationRule {
|
||||
clusterRoleSelectors?: LabelSelector;
|
||||
}
|
||||
@@ -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[];
|
||||
}
|
||||
@@ -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 Condition {
|
||||
lastTransitionTime: string;
|
||||
message: string;
|
||||
observedGeneration?: number;
|
||||
reason: string;
|
||||
status: string;
|
||||
type: string;
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 CrossVersionObjectReference {
|
||||
kind: string;
|
||||
name: string;
|
||||
apiVersion: string;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 "../api-types";
|
||||
|
||||
export interface EnvSource extends LocalObjectReference {
|
||||
/**
|
||||
* Whether the object must be defined
|
||||
*/
|
||||
optional?: boolean;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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[];
|
||||
}
|
||||
@@ -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 ExternalDocumentation {
|
||||
description?: string;
|
||||
url?: string;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* 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 "./cross-version-object-reference";
|
||||
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";
|
||||
@@ -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 { KubeObjectScope, KubeTemplateObjectMetadata } from "../api-types";
|
||||
import type { JobSpec } from "../specifics/job";
|
||||
|
||||
export interface JobTemplateSpec {
|
||||
metadata?: KubeTemplateObjectMetadata<KubeObjectScope.Namespace>;
|
||||
spec?: JobSpec;
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { JsonValue } from "type-fest";
|
||||
import type { ExternalDocumentation } from "./external-documentation";
|
||||
|
||||
/**
|
||||
* (?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$
|
||||
*/
|
||||
export type UUIDRegexString = string;
|
||||
|
||||
/**
|
||||
* (?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?3[0-9a-f]{3}-?[0-9a-f]{4}-?[0-9a-f]{12}$
|
||||
*/
|
||||
export type UUID3RegexString = string;
|
||||
|
||||
/**
|
||||
* (?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?4[0-9a-f]{3}-?[89ab][0-9a-f]{3}-?[0-9a-f]{12}$
|
||||
*/
|
||||
export type UUID4RegexString = string;
|
||||
|
||||
/**
|
||||
* (?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?5[0-9a-f]{3}-?[89ab][0-9a-f]{3}-?[0-9a-f]{12}$
|
||||
*/
|
||||
export type UUID5RegexString = string;
|
||||
|
||||
/* eslint-disable max-len */
|
||||
/**
|
||||
* ^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11})$
|
||||
*/
|
||||
export type CreditCardRegexString = string;
|
||||
/* eslint-enable max-len */
|
||||
|
||||
export interface JSONSchemaProps {
|
||||
$ref?: string;
|
||||
$schema?: string;
|
||||
additionalItems?: JSONSchemaProps | boolean;
|
||||
additionalProperties?: JSONSchemaProps | boolean;
|
||||
allOf?: JSONSchemaProps[];
|
||||
anyOf?: JSONSchemaProps[];
|
||||
|
||||
/**
|
||||
* default is a default value for undefined object fields.
|
||||
* Defaulting is a beta feature under the CustomResourceDefaulting feature gate.
|
||||
* Defaulting requires spec.preserveUnknownFields to be false.
|
||||
*/
|
||||
_default?: object;
|
||||
|
||||
definitions?: Partial<Record<string, JSONSchemaProps>>;
|
||||
dependencies?: Partial<Record<string, object>>;
|
||||
description?: string;
|
||||
_enum?: object[];
|
||||
example?: JsonValue;
|
||||
|
||||
exclusiveMaximum?: boolean;
|
||||
exclusiveMinimum?: boolean;
|
||||
externalDocs?: ExternalDocumentation;
|
||||
|
||||
/**
|
||||
* format is an OpenAPI v3 format string.
|
||||
* Unknown formats are ignored.
|
||||
*
|
||||
* The following formats are validated:
|
||||
* - bsonobjectid: a bson object ID, i.e. a 24 characters hex string
|
||||
* - uri: an URI as parsed by Golang net/url.ParseRequestURI
|
||||
* - email: an email address as parsed by Golang net/mail.ParseAddress
|
||||
* - hostname: a valid representation for an Internet host name, as defined by RFC 1034, section 3.1 [RFC1034].
|
||||
* - ipv4: an IPv4 IP as parsed by Golang net.ParseIP
|
||||
* - ipv6: an IPv6 IP as parsed by Golang net.ParseIP
|
||||
* - cidr: a CIDR as parsed by Golang net.ParseCIDR
|
||||
* - mac: a MAC address as parsed by Golang net.ParseMAC
|
||||
* - uuid: an UUID that allows uppercase defined by the regex {@link UUIDRegexString}
|
||||
* - uuid3: an UUID3 that allows uppercase defined by the regex {@link UUID3RegexString}
|
||||
* - uuid4: an UUID4 that allows uppercase defined by the regex {@link UUID4RegexString}
|
||||
* - uuid5: an UUID5 that allows uppercase defined by the regex {@link UUID5RegexString}
|
||||
* - isbn: an ISBN10 or ISBN13 number string like "0321751043" or "978-0321751041"
|
||||
* - isbn10: an ISBN10 number string like "0321751043"
|
||||
* - isbn13: an ISBN13 number string like "978-0321751041"
|
||||
* - creditcard: a credit card number defined by the regex {@link CreditCardRegexString}
|
||||
* - ssn: a U.S. social security number following the regex ^\\d{3}[- ]?\\d{2}[- ]?\\d{4}$
|
||||
* - hexcolor: an hexadecimal color code like "#FFFFFF: following the regex ^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$
|
||||
* - rgbcolor: an RGB color code like rgb like "rgb(255,255,2559"
|
||||
* - byte: base64 encoded binary data
|
||||
* - password: any kind of string
|
||||
* - date: a date string like "2006-01-02" as defined by full-date in RFC3339
|
||||
* - duration: a duration string like "22 ns" as parsed by Golang time.ParseDuration or with Scala's duration format
|
||||
* - datetime: a date time string like "2014-12-15T19:30:20.000Z" as defined by date-time in RFC3339.
|
||||
*/
|
||||
format?: string;
|
||||
|
||||
id?: string;
|
||||
items?: JSONSchemaProps | JSONSchemaProps[];
|
||||
maxItems?: number;
|
||||
maxLength?: number;
|
||||
maxProperties?: number;
|
||||
maximum?: number;
|
||||
minItems?: number;
|
||||
minLength?: number;
|
||||
minProperties?: number;
|
||||
minimum?: number;
|
||||
multipleOf?: number;
|
||||
not?: JSONSchemaProps;
|
||||
nullable?: boolean;
|
||||
oneOf?: JSONSchemaProps[];
|
||||
pattern?: string;
|
||||
patternProperties?: Partial<Record<string, JSONSchemaProps>>;
|
||||
properties?: Partial<Record<string, JSONSchemaProps>>;
|
||||
required?: Array<string>;
|
||||
title?: string;
|
||||
type?: string;
|
||||
uniqueItems?: boolean;
|
||||
x_kubernetes_embedded_resource?: boolean;
|
||||
x_kubernetes_int_or_string?: boolean;
|
||||
x_kubernetes_list_map_keys?: string[];
|
||||
x_kubernetes_list_type?: string;
|
||||
x_kubernetes_map_type?: string;
|
||||
x_kubernetes_preserve_unknown_fields?: boolean;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 { KubeObjectScope, KubeTemplateObjectMetadata } from "../api-types";
|
||||
import type { PersistentVolumeSpec } from "../specifics/persistent-volume";
|
||||
|
||||
export interface PersistentVolumeClaimTemplateSpec {
|
||||
metadata?: KubeTemplateObjectMetadata<KubeObjectScope.Cluster>;
|
||||
spec?: PersistentVolumeSpec;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* 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 "../specifics/pod";
|
||||
|
||||
export interface PodSecurityContext {
|
||||
fsGroup?: number;
|
||||
fsGroupChangePolicy?: string;
|
||||
runAsGroup?: number;
|
||||
runAsNonRoot?: boolean;
|
||||
runAsUser?: number;
|
||||
seLinuxOptions?: SeLinuxOptions;
|
||||
seccompProfile?: SeccompProfile;
|
||||
supplementalGroups?: number[];
|
||||
sysctls?: Sysctl;
|
||||
windowsOptions?: WindowsSecurityContextOptions;
|
||||
}
|
||||
@@ -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 { KubeObjectScope, KubeTemplateObjectMetadata } from "../api-types";
|
||||
import type { PodSpec } from "../specifics/pod";
|
||||
|
||||
export interface PodTemplateSpec {
|
||||
metadata?: KubeTemplateObjectMetadata<KubeObjectScope.Namespace>;
|
||||
spec?: PodSpec;
|
||||
}
|
||||
@@ -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 PolicyRule {
|
||||
verbs: string[];
|
||||
apiGroups?: string[];
|
||||
resources?: string[];
|
||||
resourceNames?: string[];
|
||||
nonResourceURLs?: string[];
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
export type PreemptionPolicy = "Never" | "PreemptLowerPriority";
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
/**
|
||||
* ResourceRequirements describes the compute resource requirements.
|
||||
*/
|
||||
export interface ResourceRequirements {
|
||||
/**
|
||||
* Limits describes the maximum amount of compute resources allowed.
|
||||
*
|
||||
* More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
|
||||
*/
|
||||
limits?: Partial<Record<string, string>>;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
|
||||
*/
|
||||
requests?: Partial<Record<string, string>>;
|
||||
}
|
||||
@@ -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 RoleRef {
|
||||
apiGroup: string;
|
||||
kind: string;
|
||||
name: string;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @default 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 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;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
export type SubjectKind = "Group" | "ServiceAccount" | "User";
|
||||
|
||||
export interface Subject {
|
||||
apiGroup?: string;
|
||||
kind: SubjectKind;
|
||||
name: string;
|
||||
namespace?: string;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import {
|
||||
isObject,
|
||||
hasTypedProperty,
|
||||
isString,
|
||||
hasOptionalTypedProperty,
|
||||
bindPredicate,
|
||||
isTypedArray,
|
||||
isRecord,
|
||||
} from "@k8slens/utilities";
|
||||
import type {
|
||||
KubeJsonApiData,
|
||||
KubeObjectMetadata,
|
||||
KubeObjectScope,
|
||||
KubeJsonApiListMetadata,
|
||||
KubeJsonApiObjectMetadata,
|
||||
KubeJsonApiDataList,
|
||||
} from "./api-types";
|
||||
import type { KubeObject } from "./kube-object";
|
||||
|
||||
const resourceApplierAnnotationsForFiltering = ["kubectl.kubernetes.io/last-applied-configuration"];
|
||||
|
||||
export const filterOutResourceApplierAnnotations = (label: string) =>
|
||||
!resourceApplierAnnotationsForFiltering.some((key) => label.startsWith(key));
|
||||
|
||||
export function isKubeObjectNonSystem(
|
||||
item: KubeJsonApiData | KubeObject<KubeObjectMetadata<KubeObjectScope>, unknown, unknown>,
|
||||
) {
|
||||
return !item.metadata.name?.startsWith("system:");
|
||||
}
|
||||
|
||||
export function isJsonApiData(object: unknown): object is KubeJsonApiData {
|
||||
return (
|
||||
isObject(object) &&
|
||||
hasTypedProperty(object, "kind", isString) &&
|
||||
hasTypedProperty(object, "apiVersion", isString) &&
|
||||
hasTypedProperty(object, "metadata", isKubeJsonApiMetadata)
|
||||
);
|
||||
}
|
||||
|
||||
export function isKubeJsonApiListMetadata(object: unknown): object is KubeJsonApiListMetadata {
|
||||
return (
|
||||
isObject(object) &&
|
||||
hasOptionalTypedProperty(object, "resourceVersion", isString) &&
|
||||
hasOptionalTypedProperty(object, "selfLink", isString)
|
||||
);
|
||||
}
|
||||
|
||||
export function isKubeJsonApiMetadata(object: unknown): object is KubeJsonApiObjectMetadata {
|
||||
return (
|
||||
isObject(object) &&
|
||||
hasTypedProperty(object, "uid", isString) &&
|
||||
hasTypedProperty(object, "name", isString) &&
|
||||
hasTypedProperty(object, "resourceVersion", isString) &&
|
||||
hasOptionalTypedProperty(object, "selfLink", isString) &&
|
||||
hasOptionalTypedProperty(object, "namespace", isString) &&
|
||||
hasOptionalTypedProperty(object, "creationTimestamp", isString) &&
|
||||
hasOptionalTypedProperty(object, "continue", isString) &&
|
||||
hasOptionalTypedProperty(object, "finalizers", bindPredicate(isTypedArray, isString)) &&
|
||||
hasOptionalTypedProperty(object, "labels", bindPredicate(isRecord, isString, isString)) &&
|
||||
hasOptionalTypedProperty(object, "annotations", bindPredicate(isRecord, isString, isString))
|
||||
);
|
||||
}
|
||||
|
||||
export function isPartialJsonApiMetadata(object: unknown): object is Partial<KubeJsonApiObjectMetadata> {
|
||||
return (
|
||||
isObject(object) &&
|
||||
hasOptionalTypedProperty(object, "uid", isString) &&
|
||||
hasOptionalTypedProperty(object, "name", isString) &&
|
||||
hasOptionalTypedProperty(object, "resourceVersion", isString) &&
|
||||
hasOptionalTypedProperty(object, "selfLink", isString) &&
|
||||
hasOptionalTypedProperty(object, "namespace", isString) &&
|
||||
hasOptionalTypedProperty(object, "creationTimestamp", isString) &&
|
||||
hasOptionalTypedProperty(object, "continue", isString) &&
|
||||
hasOptionalTypedProperty(object, "finalizers", bindPredicate(isTypedArray, isString)) &&
|
||||
hasOptionalTypedProperty(object, "labels", bindPredicate(isRecord, isString, isString)) &&
|
||||
hasOptionalTypedProperty(object, "annotations", bindPredicate(isRecord, isString, isString))
|
||||
);
|
||||
}
|
||||
|
||||
export function isPartialJsonApiData(object: unknown): object is Partial<KubeJsonApiData> {
|
||||
return (
|
||||
isObject(object) &&
|
||||
hasOptionalTypedProperty(object, "kind", isString) &&
|
||||
hasOptionalTypedProperty(object, "apiVersion", isString) &&
|
||||
hasOptionalTypedProperty(object, "metadata", isPartialJsonApiMetadata)
|
||||
);
|
||||
}
|
||||
|
||||
export function isJsonApiDataList<T>(
|
||||
object: unknown,
|
||||
verifyItem: (val: unknown) => val is T,
|
||||
): object is KubeJsonApiDataList<T> {
|
||||
return (
|
||||
isObject(object) &&
|
||||
hasTypedProperty(object, "kind", isString) &&
|
||||
hasTypedProperty(object, "apiVersion", isString) &&
|
||||
hasTypedProperty(object, "metadata", isKubeJsonApiListMetadata) &&
|
||||
hasTypedProperty(object, "items", bindPredicate(isTypedArray, verifyItem))
|
||||
);
|
||||
}
|
||||
|
||||
export function stringifyLabels(labels?: Partial<Record<string, string>>): string[] {
|
||||
if (!labels) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Object.entries(labels).map(([name, value]) => `${name}=${value}`);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "@k8slens/typescript/config/base.json",
|
||||
"include": ["**/*.ts"]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require("@k8slens/webpack").configForReact;
|
||||
Reference in new issue
Block a user