mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Support using Eviction API when deleting Pods and Deployments, fix #5602
Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
parent
763816f71f
commit
b5187ba75f
@ -3,13 +3,24 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { DerivedKubeApiOptions, KubeApiDependencies, ResourceDescriptor } from "../kube-api";
|
||||
import type {
|
||||
DeleteResourceDescriptor,
|
||||
DerivedKubeApiOptions,
|
||||
KubeApiDependencies,
|
||||
ResourceDescriptor,
|
||||
} from "../kube-api";
|
||||
import { KubeApi } from "../kube-api";
|
||||
import type { RequireExactlyOne } from "type-fest";
|
||||
import type { KubeObjectMetadata, LocalObjectReference, Affinity, Toleration, NamespaceScopedMetadata } from "../kube-object";
|
||||
import type {
|
||||
Affinity,
|
||||
KubeObjectMetadata, KubeStatusData,
|
||||
LocalObjectReference,
|
||||
NamespaceScopedMetadata,
|
||||
Toleration,
|
||||
} from "../kube-object";
|
||||
import { isKubeStatusData, KubeObject, KubeStatus } from "../kube-object";
|
||||
import type { SecretReference } from "./secret.api";
|
||||
import type { PersistentVolumeClaimSpec } from "./persistent-volume-claim.api";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import { isDefined } from "@k8slens/utilities";
|
||||
import type { PodSecurityContext } from "./types/pod-security-context";
|
||||
import type { Probe } from "./types/probe";
|
||||
@ -24,6 +35,38 @@ export class PodApi extends KubeApi<Pod> {
|
||||
});
|
||||
}
|
||||
|
||||
async evict(resource: DeleteResourceDescriptor) {
|
||||
await this.checkPreferredVersion();
|
||||
const apiUrl = this.formatUrlForNotListing(resource);
|
||||
let response: KubeStatusData;
|
||||
|
||||
try {
|
||||
response = await this.request.post<KubeStatusData>(`${apiUrl}/eviction`, {
|
||||
data: {
|
||||
apiVersion: "policy/v1",
|
||||
kind: "Eviction",
|
||||
metadata: {
|
||||
...resource,
|
||||
},
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
response = err as KubeStatusData;
|
||||
}
|
||||
|
||||
if (isKubeStatusData(response)) {
|
||||
const status = new KubeStatus(response);
|
||||
|
||||
if (status.code >= 200 && status.code < 300) {
|
||||
return status.getMessage();
|
||||
} else {
|
||||
throw status.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
async getLogs(params: ResourceDescriptor, query?: PodLogsQuery): Promise<string> {
|
||||
const path = `${this.getUrl(params)}/log`;
|
||||
|
||||
|
||||
@ -583,6 +583,15 @@ export class KubeApi<
|
||||
return parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Some k8s resources might implement special "delete" (e.g. pod.api)
|
||||
* See also: https://kubernetes.io/docs/concepts/scheduling-eviction/api-eviction/
|
||||
* By default should work same as KubeObject.remove()
|
||||
*/
|
||||
async evict(desc: DeleteResourceDescriptor): Promise<KubeStatus | KubeObject | unknown> {
|
||||
return this.delete(desc);
|
||||
}
|
||||
|
||||
async delete({ propagationPolicy = "Background", ...desc }: DeleteResourceDescriptor) {
|
||||
await this.checkPreferredVersion();
|
||||
const apiUrl = this.formatUrlForNotListing(desc);
|
||||
|
||||
@ -388,7 +388,9 @@ export abstract class KubeObjectStore<
|
||||
}
|
||||
|
||||
async remove(item: K) {
|
||||
await this.api.delete({ name: item.getName(), namespace: item.getNs() });
|
||||
// Some k8s apis might implement special more fine-grained "delete" request for resources (e.g. pod.api.ts)
|
||||
// See also: https://kubernetes.io/docs/concepts/scheduling-eviction/api-eviction/
|
||||
await this.api.evict({ name: item.getName(), namespace: item.getNs() });
|
||||
this.selectedItemsIds.delete(item.getId());
|
||||
}
|
||||
|
||||
|
||||
@ -6,17 +6,35 @@
|
||||
// Base class for all kubernetes objects
|
||||
|
||||
import moment from "moment";
|
||||
import type { KubeJsonApiData, KubeJsonApiDataList, KubeJsonApiListMetadata } from "./kube-json-api";
|
||||
import { formatDuration, hasOptionalTypedProperty, hasTypedProperty, isObject, isString, isNumber, bindPredicate, isTypedArray, isRecord } from "@k8slens/utilities";
|
||||
import type {
|
||||
KubeJsonApiData,
|
||||
KubeJsonApiDataList,
|
||||
KubeJsonApiListMetadata,
|
||||
} from "./kube-json-api";
|
||||
import {
|
||||
formatDuration,
|
||||
hasOptionalTypedProperty,
|
||||
hasTypedProperty,
|
||||
isObject,
|
||||
isString,
|
||||
isNumber,
|
||||
bindPredicate,
|
||||
isTypedArray,
|
||||
isRecord,
|
||||
} from "@k8slens/utilities";
|
||||
import type { ItemObject } from "../item.store";
|
||||
import type { Patch } from "rfc6902";
|
||||
import assert from "assert";
|
||||
import type { JsonObject } from "type-fest";
|
||||
import requestKubeObjectPatchInjectable from "./endpoints/resource-applier.api/request-patch.injectable";
|
||||
import requestKubeObjectPatchInjectable
|
||||
from "./endpoints/resource-applier.api/request-patch.injectable";
|
||||
import { apiKubeInjectionToken } from "./api-kube";
|
||||
import requestKubeObjectCreationInjectable from "./endpoints/resource-applier.api/request-update.injectable";
|
||||
import requestKubeObjectCreationInjectable
|
||||
from "./endpoints/resource-applier.api/request-update.injectable";
|
||||
import { dump } from "js-yaml";
|
||||
import { getLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
||||
import {
|
||||
getLegacyGlobalDiForExtensionApi,
|
||||
} from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
||||
import autoBind from "auto-bind";
|
||||
|
||||
export type KubeJsonApiDataFor<K> = K extends KubeObject<infer Metadata, infer Status, infer Spec>
|
||||
@ -203,13 +221,17 @@ export interface BaseKubeJsonApiObjectMetadata<Namespaced extends KubeObjectScop
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export type KubeJsonApiObjectMetadata<Namespaced extends KubeObjectScope = KubeObjectScope> = BaseKubeJsonApiObjectMetadata<Namespaced> & (
|
||||
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> & {
|
||||
export type KubeObjectMetadata<Namespaced extends KubeObjectScope = KubeObjectScope> =
|
||||
KubeJsonApiObjectMetadata<Namespaced>
|
||||
& {
|
||||
readonly selfLink: string;
|
||||
readonly uid: string;
|
||||
readonly name: string;
|
||||
@ -225,6 +247,25 @@ export interface KubeStatusData {
|
||||
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 function isKubeStatusData(object: unknown): object is KubeStatusData {
|
||||
@ -232,8 +273,11 @@ export function isKubeStatusData(object: unknown): object is KubeStatusData {
|
||||
&& hasTypedProperty(object, "kind", isString)
|
||||
&& hasTypedProperty(object, "apiVersion", isString)
|
||||
&& hasTypedProperty(object, "code", isNumber)
|
||||
&& hasOptionalTypedProperty(object, "message", isString)
|
||||
&& hasOptionalTypedProperty(object, "reason", isString)
|
||||
&& (
|
||||
hasOptionalTypedProperty(object, "message", isString)
|
||||
|| hasOptionalTypedProperty(object, "reason", isString)
|
||||
|| hasOptionalTypedProperty(object, "status", isString)
|
||||
)
|
||||
&& object.kind === "Status";
|
||||
}
|
||||
|
||||
@ -241,14 +285,22 @@ 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 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 || "";
|
||||
}
|
||||
|
||||
getMessage() {
|
||||
const { code, message, reason, status } = this;
|
||||
|
||||
return `${code}: ${message ?? reason ?? status}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -31,8 +31,6 @@ import { observer } from "mobx-react";
|
||||
|
||||
export interface KubeObjectMenuProps<TKubeObject extends KubeObject> extends MenuActionsProps {
|
||||
object: TKubeObject;
|
||||
editable?: boolean;
|
||||
removable?: boolean;
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
@ -86,8 +84,6 @@ class NonInjectedKubeObjectMenu<Kube extends KubeObject> extends React.Component
|
||||
private emitOnContextMenuOpen(object: KubeObject) {
|
||||
const {
|
||||
apiManager,
|
||||
editable,
|
||||
removable,
|
||||
hideDetails,
|
||||
createEditResourceTab,
|
||||
withConfirmation,
|
||||
@ -98,8 +94,8 @@ class NonInjectedKubeObjectMenu<Kube extends KubeObject> extends React.Component
|
||||
} = this.props;
|
||||
|
||||
const store = apiManager.getStore(object.selfLink);
|
||||
const isEditable = editable ?? (Boolean(store?.patch) || Boolean(updateAction));
|
||||
const isRemovable = removable ?? (Boolean(store?.remove) || Boolean(removeAction));
|
||||
const isEditable = Boolean(updateAction ?? store?.patch);
|
||||
const isRemovable = Boolean(removeAction ?? store?.remove);
|
||||
|
||||
runInAction(() => {
|
||||
this.menuItems.clear();
|
||||
@ -177,8 +173,6 @@ class NonInjectedKubeObjectMenu<Kube extends KubeObject> extends React.Component
|
||||
render() {
|
||||
const {
|
||||
className,
|
||||
editable,
|
||||
removable,
|
||||
object,
|
||||
removeAction, // This is here so we don't pass it down to `<MenuAction>`
|
||||
removeConfirmationMessage, // This is here so we don't pass it down to `<MenuAction>`
|
||||
|
||||
Loading…
Reference in New Issue
Block a user