mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Move lookupApi into apiManager (#3540)
This commit is contained in:
parent
574aea40b4
commit
ee158e33df
@ -41,7 +41,7 @@ import { InstalledExtension, ExtensionDiscovery } from "../extensions/extension-
|
||||
import type { LensExtensionId } from "../extensions/lens-extension";
|
||||
import { installDeveloperTools } from "./developer-tools";
|
||||
import { LensProtocolRouterMain } from "./protocol-handler";
|
||||
import { disposer, getAppVersion, getAppVersionFromProxyServer } from "../common/utils";
|
||||
import { disposer, getAppVersion, getAppVersionFromProxyServer, storedKubeConfigFolder } from "../common/utils";
|
||||
import { bindBroadcastHandlers, ipcMainOn } from "../common/ipc";
|
||||
import { startUpdateChecking } from "./app-updater";
|
||||
import { IpcRendererNavigationEvents } from "../renderer/navigation/events";
|
||||
@ -213,7 +213,7 @@ app.on("ready", async () => {
|
||||
|
||||
ipcMainOn(IpcRendererNavigationEvents.LOADED, async () => {
|
||||
cleanup.push(pushCatalogToRenderer(catalogEntityRegistry));
|
||||
await ensureDir(ClusterStore.storedKubeConfigFolder);
|
||||
await ensureDir(storedKubeConfigFolder());
|
||||
KubeconfigSyncManager.getInstance().startSync();
|
||||
startUpdateChecking();
|
||||
LensProtocolRouterMain.getInstance().rendererLoaded = true;
|
||||
|
||||
@ -23,8 +23,9 @@ import type { KubeObjectStore } from "../kube-object.store";
|
||||
|
||||
import { action, observable, makeObservable } from "mobx";
|
||||
import { autoBind, iter } from "../utils";
|
||||
import { KubeApi, parseKubeApi } from "./kube-api";
|
||||
import type { KubeApi } from "./kube-api";
|
||||
import type { KubeObject } from "./kube-object";
|
||||
import { IKubeObjectRef, parseKubeApi, createKubeApiURL } from "./kube-api-parse";
|
||||
|
||||
export class ApiManager {
|
||||
private apis = observable.map<string, KubeApi<KubeObject>>();
|
||||
@ -91,6 +92,45 @@ export class ApiManager {
|
||||
getStore<S extends KubeObjectStore<KubeObject>>(api: string | KubeApi<KubeObject>): S | undefined {
|
||||
return this.stores.get(this.resolveApi(api)?.apiBase) as S;
|
||||
}
|
||||
|
||||
lookupApiLink(ref: IKubeObjectRef, parentObject: KubeObject): string {
|
||||
const {
|
||||
kind, apiVersion, name,
|
||||
namespace = parentObject.getNs()
|
||||
} = ref;
|
||||
|
||||
if (!kind) return "";
|
||||
|
||||
// search in registered apis by 'kind' & 'apiVersion'
|
||||
const api = this.getApi(api => api.kind === kind && api.apiVersionWithGroup == apiVersion);
|
||||
|
||||
if (api) {
|
||||
return api.getUrl({ namespace, name });
|
||||
}
|
||||
|
||||
// lookup api by generated resource link
|
||||
const apiPrefixes = ["/apis", "/api"];
|
||||
const resource = kind.endsWith("s") ? `${kind.toLowerCase()}es` : `${kind.toLowerCase()}s`;
|
||||
|
||||
for (const apiPrefix of apiPrefixes) {
|
||||
const apiLink = createKubeApiURL({ apiPrefix, apiVersion, name, namespace, resource });
|
||||
|
||||
if (this.getApi(apiLink)) {
|
||||
return apiLink;
|
||||
}
|
||||
}
|
||||
|
||||
// resolve by kind only (hpa's might use refs to older versions of resources for example)
|
||||
const apiByKind = this.getApi(api => api.kind === kind);
|
||||
|
||||
if (apiByKind) {
|
||||
return apiByKind.getUrl({ name, namespace });
|
||||
}
|
||||
|
||||
// otherwise generate link with default prefix
|
||||
// resource still might exists in k8s, but api is not registered in the app
|
||||
return createKubeApiURL({ apiVersion, name, namespace, resource });
|
||||
}
|
||||
}
|
||||
|
||||
export const apiManager = new ApiManager();
|
||||
|
||||
@ -20,35 +20,21 @@
|
||||
*/
|
||||
|
||||
import jsYaml from "js-yaml";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import type { KubeJsonApiData } from "../kube-json-api";
|
||||
import { apiBase } from "../index";
|
||||
import { apiManager } from "../api-manager";
|
||||
|
||||
export const resourceApplierApi = {
|
||||
annotations: [
|
||||
"kubectl.kubernetes.io/last-applied-configuration"
|
||||
],
|
||||
|
||||
async update<K extends KubeObject>(resource: object | string): Promise<K | null> {
|
||||
async update(resource: object | string): Promise<KubeJsonApiData | null> {
|
||||
if (typeof resource === "string") {
|
||||
resource = jsYaml.safeLoad(resource);
|
||||
}
|
||||
|
||||
return apiBase
|
||||
.post<KubeJsonApiData[]>("/stack", { data: resource })
|
||||
.then(data => {
|
||||
const items = data.map(obj => {
|
||||
const api = apiManager.getApiByKind(obj.kind, obj.apiVersion);
|
||||
const [data = null] = await apiBase.post<KubeJsonApiData[]>("/stack", { data: resource });
|
||||
|
||||
if (api) {
|
||||
return new api.objectConstructor(obj);
|
||||
} else {
|
||||
return new KubeObject(obj);
|
||||
}
|
||||
});
|
||||
|
||||
return items[0] as K ?? null;
|
||||
});
|
||||
return data;
|
||||
}
|
||||
};
|
||||
|
||||
@ -21,9 +21,7 @@
|
||||
|
||||
// Parse kube-api path and get api-version, group, etc.
|
||||
|
||||
import type { KubeObject } from "./kube-object";
|
||||
import { splitArray } from "../../common/utils";
|
||||
import { apiManager } from "./api-manager";
|
||||
import { isDebugging } from "../../common/vars";
|
||||
import logger from "../../main/logger";
|
||||
import { inspect } from "util";
|
||||
@ -159,42 +157,3 @@ export function createKubeApiURL(ref: IKubeApiLinkRef): string {
|
||||
.filter(v => v)
|
||||
.join("/");
|
||||
}
|
||||
|
||||
export function lookupApiLink(ref: IKubeObjectRef, parentObject: KubeObject): string {
|
||||
const {
|
||||
kind, apiVersion, name,
|
||||
namespace = parentObject.getNs()
|
||||
} = ref;
|
||||
|
||||
if (!kind) return "";
|
||||
|
||||
// search in registered apis by 'kind' & 'apiVersion'
|
||||
const api = apiManager.getApi(api => api.kind === kind && api.apiVersionWithGroup == apiVersion);
|
||||
|
||||
if (api) {
|
||||
return api.getUrl({ namespace, name });
|
||||
}
|
||||
|
||||
// lookup api by generated resource link
|
||||
const apiPrefixes = ["/apis", "/api"];
|
||||
const resource = kind.endsWith("s") ? `${kind.toLowerCase()}es` : `${kind.toLowerCase()}s`;
|
||||
|
||||
for (const apiPrefix of apiPrefixes) {
|
||||
const apiLink = createKubeApiURL({ apiPrefix, apiVersion, name, namespace, resource });
|
||||
|
||||
if (apiManager.getApi(apiLink)) {
|
||||
return apiLink;
|
||||
}
|
||||
}
|
||||
|
||||
// resolve by kind only (hpa's might use refs to older versions of resources for example)
|
||||
const apiByKind = apiManager.getApi(api => api.kind === kind);
|
||||
|
||||
if (apiByKind) {
|
||||
return apiByKind.getUrl({ name, namespace });
|
||||
}
|
||||
|
||||
// otherwise generate link with default prefix
|
||||
// resource still might exists in k8s, but api is not registered in the app
|
||||
return createKubeApiURL({ apiVersion, name, namespace, resource });
|
||||
}
|
||||
|
||||
@ -56,11 +56,6 @@ export interface IKubeApiOptions<T extends KubeObject> {
|
||||
checkPreferredVersion?: boolean;
|
||||
}
|
||||
|
||||
export interface KubeApiListOptions {
|
||||
namespace?: string;
|
||||
reqInit?: RequestInit;
|
||||
}
|
||||
|
||||
export interface IKubeApiQueryParams {
|
||||
watch?: boolean | number;
|
||||
resourceVersion?: string;
|
||||
@ -506,5 +501,3 @@ export class KubeApi<T extends KubeObject> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export * from "./kube-api-parse";
|
||||
|
||||
@ -287,14 +287,14 @@ export class KubeObject<Metadata extends KubeObjectMetadata = KubeObjectMetadata
|
||||
}
|
||||
|
||||
// use unified resource-applier api for updating all k8s objects
|
||||
async update<K extends KubeObject>(data: Partial<K>): Promise<K> {
|
||||
async update(data: Partial<this>): Promise<KubeJsonApiData | null> {
|
||||
for (const field of KubeObject.nonEditableFields) {
|
||||
if (!_.isEqual(_.get(this, field), _.get(data, field))) {
|
||||
throw new Error(`Failed to update Kube Object: ${field} has been modified`);
|
||||
}
|
||||
}
|
||||
|
||||
return resourceApplierApi.update<K>({
|
||||
return resourceApplierApi.update({
|
||||
...this.toPlainObject(),
|
||||
...data,
|
||||
});
|
||||
|
||||
@ -33,7 +33,7 @@ import { boundMethod, cssNames, prevDefault } from "../../utils";
|
||||
import type { ItemObject } from "../../item.store";
|
||||
import { Spinner } from "../spinner";
|
||||
import { ThemeStore } from "../../theme.store";
|
||||
import { lookupApiLink } from "../../api/kube-api";
|
||||
import { apiManager } from "../../api/api-manager";
|
||||
import { kubeSelectedUrlParam, showDetails } from "../kube-detail-params";
|
||||
import { kubeWatchApi } from "../../api/kube-watch-api";
|
||||
|
||||
@ -106,7 +106,7 @@ export class ClusterIssues extends React.Component<Props> {
|
||||
age: getAge(),
|
||||
message,
|
||||
kind,
|
||||
selfLink: lookupApiLink(involvedObject, error),
|
||||
selfLink: apiManager.lookupApiLink(involvedObject, error),
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@ import type { KubeObjectDetailsProps } from "../kube-object-details";
|
||||
import { cssNames } from "../../utils";
|
||||
import { HorizontalPodAutoscaler, HpaMetricType, IHpaMetric } from "../../api/endpoints/hpa.api";
|
||||
import { Table, TableCell, TableHead, TableRow } from "../table";
|
||||
import { lookupApiLink } from "../../api/kube-api";
|
||||
import { apiManager } from "../../api/api-manager";
|
||||
import { KubeObjectMeta } from "../kube-object-meta";
|
||||
import { getDetailsUrl } from "../kube-detail-params";
|
||||
|
||||
@ -55,7 +55,7 @@ export class HpaDetails extends React.Component<HpaDetailsProps> {
|
||||
case HpaMetricType.Object:
|
||||
const { target } = metric.object;
|
||||
const { kind, name } = target;
|
||||
const objectUrl = getDetailsUrl(lookupApiLink(target, hpa));
|
||||
const objectUrl = getDetailsUrl(apiManager.lookupApiLink(target, hpa));
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -108,7 +108,7 @@ export class HpaDetails extends React.Component<HpaDetailsProps> {
|
||||
|
||||
<DrawerItem name="Reference">
|
||||
{scaleTargetRef && (
|
||||
<Link to={getDetailsUrl(lookupApiLink(scaleTargetRef, hpa))}>
|
||||
<Link to={getDetailsUrl(apiManager.lookupApiLink(scaleTargetRef, hpa))}>
|
||||
{scaleTargetRef.kind}/{scaleTargetRef.name}
|
||||
</Link>
|
||||
)}
|
||||
|
||||
@ -30,7 +30,7 @@ import type { KubeObjectDetailsProps } from "../kube-object-details";
|
||||
import type { KubeEvent } from "../../api/endpoints/events.api";
|
||||
import { KubeObjectMeta } from "../kube-object-meta";
|
||||
import { Table, TableCell, TableHead, TableRow } from "../table";
|
||||
import { lookupApiLink } from "../../api/kube-api";
|
||||
import { apiManager } from "../../api/api-manager";
|
||||
import { LocaleDate } from "../locale-date";
|
||||
import { getDetailsUrl } from "../kube-detail-params";
|
||||
|
||||
@ -82,7 +82,7 @@ export class EventDetails extends React.Component<Props> {
|
||||
</TableHead>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Link to={getDetailsUrl(lookupApiLink(involvedObject, event))}>
|
||||
<Link to={getDetailsUrl(apiManager.lookupApiLink(involvedObject, event))}>
|
||||
{name}
|
||||
</Link>
|
||||
</TableCell>
|
||||
|
||||
@ -35,7 +35,7 @@ import { Tooltip } from "../tooltip";
|
||||
import { Link } from "react-router-dom";
|
||||
import { cssNames, IClassName, stopPropagation } from "../../utils";
|
||||
import { Icon } from "../icon";
|
||||
import { lookupApiLink } from "../../api/kube-api";
|
||||
import { apiManager } from "../../api/api-manager";
|
||||
import { eventsURL } from "../../../common/routes";
|
||||
import { getDetailsUrl } from "../kube-detail-params";
|
||||
|
||||
@ -196,7 +196,7 @@ export class Events extends React.Component<Props> {
|
||||
)
|
||||
},
|
||||
event.getNs(),
|
||||
<Link key="link" to={getDetailsUrl(lookupApiLink(involvedObject, event))} onClick={stopPropagation}>
|
||||
<Link key="link" to={getDetailsUrl(apiManager.lookupApiLink(involvedObject, event))} onClick={stopPropagation}>
|
||||
{involvedObject.kind}: {involvedObject.name}
|
||||
</Link>,
|
||||
event.getSource(),
|
||||
|
||||
@ -26,7 +26,7 @@ import { observer } from "mobx-react";
|
||||
import { EndpointSubset, Endpoint, EndpointAddress} from "../../api/endpoints";
|
||||
import { Table, TableCell, TableHead, TableRow } from "../table";
|
||||
import { boundMethod } from "../../utils";
|
||||
import { lookupApiLink } from "../../api/kube-api";
|
||||
import { apiManager } from "../../api/api-manager";
|
||||
import { Link } from "react-router-dom";
|
||||
import { getDetailsUrl } from "../kube-detail-params";
|
||||
|
||||
@ -92,7 +92,7 @@ export class EndpointSubsetList extends React.Component<Props> {
|
||||
<TableCell className="name">{address.hostname}</TableCell>
|
||||
<TableCell className="target">
|
||||
{ address.targetRef && (
|
||||
<Link to={getDetailsUrl(lookupApiLink(address.getTargetRef(), endpoint))}>
|
||||
<Link to={getDetailsUrl(apiManager.lookupApiLink(address.getTargetRef(), endpoint))}>
|
||||
{address.targetRef.name}
|
||||
</Link>
|
||||
)}
|
||||
|
||||
@ -35,7 +35,7 @@ import { jobStore } from "./job.store";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object-details";
|
||||
import { getMetricsForJobs, IPodMetrics, Job } from "../../api/endpoints";
|
||||
import { PodDetailsList } from "../+workloads-pods/pod-details-list";
|
||||
import { lookupApiLink } from "../../api/kube-api";
|
||||
import { apiManager } from "../../api/api-manager";
|
||||
import { KubeObjectMeta } from "../kube-object-meta";
|
||||
import { makeObservable, observable } from "mobx";
|
||||
import { podMetricTabs, PodCharts } from "../+workloads-pods/pod-charts";
|
||||
@ -117,7 +117,7 @@ export class JobDetails extends React.Component<Props> {
|
||||
{
|
||||
ownerRefs.map(ref => {
|
||||
const { name, kind } = ref;
|
||||
const detailsUrl = getDetailsUrl(lookupApiLink(ref, job));
|
||||
const detailsUrl = getDetailsUrl(apiManager.lookupApiLink(ref, job));
|
||||
|
||||
return (
|
||||
<p key={name}>
|
||||
|
||||
@ -35,7 +35,7 @@ import { cssNames, stopPropagation } from "../../utils";
|
||||
import toPairs from "lodash/toPairs";
|
||||
import startCase from "lodash/startCase";
|
||||
import kebabCase from "lodash/kebabCase";
|
||||
import { lookupApiLink } from "../../api/kube-api";
|
||||
import { apiManager } from "../../api/api-manager";
|
||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||
import { Badge } from "../badge";
|
||||
import type { PodsRouteParams } from "../../../common/routes";
|
||||
@ -135,7 +135,7 @@ export class Pods extends React.Component<Props> {
|
||||
pod.getRestartsCount(),
|
||||
pod.getOwnerRefs().map(ref => {
|
||||
const { kind, name } = ref;
|
||||
const detailsLink = getDetailsUrl(lookupApiLink(ref, pod));
|
||||
const detailsLink = getDetailsUrl(apiManager.lookupApiLink(ref, pod));
|
||||
|
||||
return (
|
||||
<Badge flat key={name} className="owner" tooltip={name}>
|
||||
|
||||
@ -105,7 +105,7 @@ export class CreateResource extends React.Component<Props> {
|
||||
await Promise.all(
|
||||
resources.map(data => {
|
||||
return resourceApplierApi.update(data)
|
||||
.then(item => createdResources.push(item.getName()))
|
||||
.then(item => createdResources.push(item.metadata.name))
|
||||
.catch((err: JsonApiErrorParsed) => errors.push(err.toString()));
|
||||
})
|
||||
);
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
import React from "react";
|
||||
import type { KubeMetaField, KubeObject } from "../../api/kube-object";
|
||||
import { DrawerItem, DrawerItemLabels } from "../drawer";
|
||||
import { lookupApiLink } from "../../api/kube-api";
|
||||
import { apiManager } from "../../api/api-manager";
|
||||
import { Link } from "react-router-dom";
|
||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||
import { LocaleDate } from "../locale-date";
|
||||
@ -93,7 +93,7 @@ export class KubeObjectMeta extends React.Component<KubeObjectMetaProps> {
|
||||
{
|
||||
ownerRefs.map(ref => {
|
||||
const { name, kind } = ref;
|
||||
const ownerDetailsUrl = getDetailsUrl(lookupApiLink(ref, object));
|
||||
const ownerDetailsUrl = getDetailsUrl(apiManager.lookupApiLink(ref, object));
|
||||
|
||||
return (
|
||||
<p key={name}>
|
||||
|
||||
@ -27,7 +27,8 @@ import { KubeObject, KubeStatus } from "./api/kube-object";
|
||||
import type { IKubeWatchEvent } from "./api/kube-watch-api";
|
||||
import { ItemStore } from "./item.store";
|
||||
import { apiManager } from "./api/api-manager";
|
||||
import { ensureObjectSelfLink, IKubeApiQueryParams, KubeApi, parseKubeApi } from "./api/kube-api";
|
||||
import { ensureObjectSelfLink, IKubeApiQueryParams, KubeApi } from "./api/kube-api";
|
||||
import { parseKubeApi } from "./api/kube-api-parse";
|
||||
import type { KubeJsonApiData } from "./api/kube-json-api";
|
||||
import { Notifications } from "./components/notifications";
|
||||
|
||||
@ -279,7 +280,8 @@ export abstract class KubeObjectStore<T extends KubeObject> extends ItemStore<T>
|
||||
}
|
||||
|
||||
async update(item: T, data: Partial<T>): Promise<T> {
|
||||
const newItem = await item.update(data);
|
||||
const rawItem = await item.update(data);
|
||||
const newItem = new this.api.objectConstructor(rawItem);
|
||||
|
||||
ensureObjectSelfLink(this.api, newItem);
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user