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

Replace resource applier with injectables

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2022-08-09 14:32:18 -04:00
parent 35f3c08279
commit 1809f98046
7 changed files with 92 additions and 64 deletions

View File

@ -1,38 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import yaml from "js-yaml";
import type { KubeJsonApiData } from "../kube-json-api";
import { apiBase } from "../index";
import type { Patch } from "rfc6902";
export const annotations = [
"kubectl.kubernetes.io/last-applied-configuration",
];
export async function update(resource: object | string): Promise<KubeJsonApiData> {
if (typeof resource === "string") {
const parsed = yaml.load(resource);
if (!parsed || typeof parsed !== "object") {
throw new Error("Cannot update resource to string or number");
}
resource = parsed;
}
return apiBase.post<KubeJsonApiData>("/stack", { data: resource });
}
export async function patch(name: string, kind: string, ns: string | undefined, patch: Patch): Promise<KubeJsonApiData> {
return apiBase.patch<KubeJsonApiData>("/stack", {
data: {
name,
kind,
ns,
patch,
},
});
}

View File

@ -0,0 +1,30 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import type { Patch } from "rfc6902";
import { apiBaseInjectionToken } from "../../api-base";
import type { KubeJsonApiData } from "../../kube-json-api";
export type RequestKubeObjectPatch = (name: string, kind: string, ns: string | undefined, patch: Patch) => Promise<KubeJsonApiData>;
const requestKubeObjectPatchInjectable = getInjectable({
id: "request-kube-object-patch",
instantiate: (di): RequestKubeObjectPatch => {
const apiBase = di.inject(apiBaseInjectionToken);
return (name, kind, ns, patch) => (
apiBase.patch("/stack", {
data: {
name,
kind,
ns,
patch,
},
})
);
},
});
export default requestKubeObjectPatchInjectable;

View File

@ -0,0 +1,20 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { apiBaseInjectionToken } from "../../api-base";
import type { KubeJsonApiData } from "../../kube-json-api";
export type RequestKubeObjectCreation = (resourceDescriptor: string) => Promise<KubeJsonApiData>;
const requestKubeObjectCreationInjectable = getInjectable({
id: "request-kube-object-creation",
instantiate: (di): RequestKubeObjectCreation => {
const apiBase = di.inject(apiBaseInjectionToken);
return (data) => apiBase.post("/stack", { data });
},
});
export default requestKubeObjectCreationInjectable;

View File

@ -9,11 +9,15 @@ import moment from "moment";
import type { KubeJsonApiData, KubeJsonApiDataList, KubeJsonApiListMetadata } from "./kube-json-api";
import { autoBind, formatDuration, hasOptionalTypedProperty, hasTypedProperty, isObject, isString, isNumber, bindPredicate, isTypedArray, isRecord, json } from "../utils";
import type { ItemObject } from "../item.store";
import { apiKube } from "./index";
import * as resourceApplierApi from "./endpoints/resource-applier.api";
import type { Patch } from "rfc6902";
import assert from "assert";
import type { JsonObject } from "type-fest";
import { asLegacyGlobalFunctionForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api";
import requestKubeObjectPatchInjectable from "./endpoints/resource-applier.api/patch.injectable";
import { asLegacyGlobalForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
import { apiKubeInjectionToken } from "./api-kube";
import requestKubeObjectCreationInjectable from "./endpoints/resource-applier.api/update.injectable";
import { dump } from "js-yaml";
export type KubeJsonApiDataFor<K> = K extends KubeObject<infer Metadata, infer Status, infer Spec>
? KubeJsonApiData<Metadata, Status, Spec>
@ -375,6 +379,12 @@ export type ScopedNamespace<Namespaced extends KubeObjectScope> = (
: string | undefined
);
const resourceApplierAnnotationsForFiltering = [
"kubectl.kubernetes.io/last-applied-configuration",
];
const filterOutResourceApplierAnnotations = (label: string) => !resourceApplierAnnotationsForFiltering.some(key => label.startsWith(key));
export class KubeObject<
Metadata extends KubeObjectMetadata<KubeObjectScope> = KubeObjectMetadata<KubeObjectScope>,
Status = unknown,
@ -588,11 +598,11 @@ export class KubeObject<
getAnnotations(filter = false): string[] {
const labels = KubeObject.stringifyLabels(this.metadata.annotations);
return filter ? labels.filter(label => {
const skip = resourceApplierApi.annotations.some(key => label.startsWith(key));
if (!filter) {
return labels;
}
return !skip;
}) : labels;
return labels.filter(filterOutResourceApplierAnnotations);
}
getOwnerRefs() {
@ -634,7 +644,9 @@ export class KubeObject<
}
}
return resourceApplierApi.patch(this.getName(), this.kind, this.getNs(), patch);
const requestKubeObjectPatch = asLegacyGlobalFunctionForExtensionApi(requestKubeObjectPatchInjectable);
return requestKubeObjectPatch(this.getName(), this.kind, this.getNs(), patch);
}
/**
@ -647,11 +659,13 @@ export class KubeObject<
* @deprecated use KubeApi.update instead
*/
async update(data: Partial<this>): Promise<KubeJsonApiData | null> {
// use unified resource-applier api for updating all k8s objects
return resourceApplierApi.update({
const requestKubeObjectCreation = asLegacyGlobalFunctionForExtensionApi(requestKubeObjectCreationInjectable);
const descriptor = dump({
...this.toPlainObject(),
...data,
});
return requestKubeObjectCreation(descriptor);
}
/**
@ -660,6 +674,8 @@ export class KubeObject<
delete(params?: object) {
assert(this.selfLink, "selfLink must be present to delete self");
const apiKube = asLegacyGlobalForExtensionApi(apiKubeInjectionToken);
return apiKube.del(this.selfLink, params);
}
}

View File

@ -62,11 +62,10 @@ export class ResourceApplier {
}
}
async apply(resource: KubernetesObject | any): Promise<string> {
resource = this.sanitizeObject(resource);
async create(resource: string): Promise<string> {
appEventBus.emit({ name: "resource", action: "apply" });
return this.kubectlApply(yaml.dump(resource));
return this.kubectlApply(yaml.dump(this.sanitizeObject(resource)));
}
protected async kubectlApply(content: string): Promise<string> {
@ -154,11 +153,7 @@ export class ResourceApplier {
delete res.status;
delete res.metadata?.resourceVersion;
const annotations = res.metadata?.annotations;
if (annotations) {
delete annotations["kubectl.kubernetes.io/last-applied-configuration"];
}
delete res.metadata?.annotations["kubectl.kubernetes.io/last-applied-configuration"];
return res;
}

View File

@ -5,17 +5,19 @@
import { getRouteInjectable } from "../../router/router.injectable";
import { apiPrefix } from "../../../common/vars";
import { ResourceApplier } from "../../resource-applier";
import { clusterRoute } from "../../router/route";
import { payloadValidatedClusterRoute } from "../../router/route";
import Joi from "joi";
const applyResourceRouteInjectable = getRouteInjectable({
const createResourceRouteInjectable = getRouteInjectable({
id: "apply-resource-route",
instantiate: () => clusterRoute({
instantiate: () => payloadValidatedClusterRoute({
method: "post",
path: `${apiPrefix}/stack`,
payloadValidator: Joi.string(),
})(async ({ cluster, payload }) => ({
response: await new ResourceApplier(cluster).apply(payload),
response: await new ResourceApplier(cluster).create(payload),
})),
});
export default applyResourceRouteInjectable;
export default createResourceRouteInjectable;

View File

@ -6,14 +6,13 @@
import React from "react";
import type { SelectOption } from "../../select";
import { Select } from "../../select";
import yaml from "js-yaml";
import yaml, { dump } from "js-yaml";
import type { IComputedValue } from "mobx";
import { makeObservable, observable } from "mobx";
import { observer } from "mobx-react";
import type { CreateResourceTabStore } from "./store";
import { EditorPanel } from "../editor-panel";
import { InfoPanel } from "../info-panel";
import * as resourceApplierApi from "../../../../common/k8s-api/endpoints/resource-applier.api";
import { Notifications } from "../../notifications";
import logger from "../../../../common/logger";
import type { ApiManager } from "../../../../common/k8s-api/api-manager";
@ -28,6 +27,8 @@ import type { GetDetailsUrl } from "../../kube-detail-params/get-details-url.inj
import apiManagerInjectable from "../../../../common/k8s-api/api-manager/manager.injectable";
import getDetailsUrlInjectable from "../../kube-detail-params/get-details-url.injectable";
import navigateInjectable from "../../../navigation/navigate.injectable";
import type { RequestKubeObjectCreation } from "../../../../common/k8s-api/endpoints/resource-applier.api/update.injectable";
import requestKubeObjectCreationInjectable from "../../../../common/k8s-api/endpoints/resource-applier.api/update.injectable";
export interface CreateResourceProps {
tabId: string;
@ -39,6 +40,7 @@ interface Dependencies {
apiManager: ApiManager;
navigate: Navigate;
getDetailsUrl: GetDetailsUrl;
requestKubeObjectCreation: RequestKubeObjectCreation;
}
@observer
@ -68,7 +70,7 @@ class NonInjectedCreateResource extends React.Component<CreateResourceProps & De
};
create = async (): Promise<void> => {
const { apiManager, getDetailsUrl, navigate } = this.props;
const { apiManager, getDetailsUrl, navigate, requestKubeObjectCreation } = this.props;
if (this.error || !this.data?.trim()) {
// do not save when field is empty or there is an error
@ -84,7 +86,7 @@ class NonInjectedCreateResource extends React.Component<CreateResourceProps & De
const creatingResources = resources.map(async (resource) => {
try {
const data = await resourceApplierApi.update(resource);
const data = await requestKubeObjectCreation(dump(resource));
const { kind, apiVersion, metadata: { name, namespace }} = data;
const showDetails = () => {
@ -168,5 +170,6 @@ export const CreateResource = withInjectables<Dependencies, CreateResourceProps>
apiManager: di.inject(apiManagerInjectable),
getDetailsUrl: di.inject(getDetailsUrlInjectable),
navigate: di.inject(navigateInjectable),
requestKubeObjectCreation: di.inject(requestKubeObjectCreationInjectable),
}),
});