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

fix deployments.store.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2022-04-12 10:08:59 -04:00
parent 5afa96971e
commit 24db61fbef
42 changed files with 349 additions and 183 deletions

View File

@ -3,13 +3,13 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import type { KubeObjectStore } from "./kube-object.store"; import type { KubeObjectStore } from "../kube-object.store";
import { action, observable, makeObservable } from "mobx"; import { action, observable, makeObservable } from "mobx";
import { autoBind, isDefined, iter } from "../utils"; import { autoBind, isDefined, iter } from "../../utils";
import type { KubeApi } from "./kube-api"; import type { KubeApi } from "../kube-api";
import type { KubeJsonApiDataFor, KubeObject, ObjectReference } from "./kube-object"; import type { KubeJsonApiDataFor, KubeObject, ObjectReference } from "../kube-object";
import { parseKubeApi, createKubeApiURL } from "./kube-api-parse"; import { parseKubeApi, createKubeApiURL } from "../kube-api-parse";
export type RegisterableStore<Store> = Store extends KubeObjectStore<any, any, any> export type RegisterableStore<Store> = Store extends KubeObjectStore<any, any, any>
? Store ? Store
@ -148,5 +148,3 @@ export class ApiManager {
return createKubeApiURL({ apiVersion, name, namespace, resource }); return createKubeApiURL({ apiVersion, name, namespace, resource });
} }
} }
export const apiManager = new ApiManager();

View File

@ -0,0 +1,7 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
export * from "./api-manager";
export * from "./legacy-global";

View File

@ -0,0 +1,11 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { ApiManager } from "./api-manager";
/**
* @deprecated use `di.inject(apiManagerInjectable)` instead
*/
export const apiManager = new ApiManager();

View File

@ -2,12 +2,17 @@
* Copyright (c) OpenLens Authors. All rights reserved. * Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { apiManager } from "../../../../common/k8s-api/api-manager";
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { apiManager } from "./legacy-global";
const apiManagerInjectable = getInjectable({ const apiManagerInjectable = getInjectable({
id: "api-manager", id: "api-manager",
instantiate: () => apiManager, instantiate: () => {
const a = apiManager;
// NOTE: this is to remove the deprecation notice
return a;
},
}); });
export default apiManagerInjectable; export default apiManagerInjectable;

View File

@ -0,0 +1,19 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { createStoresAndApisInjectionToken } from "../create-stores-apis.token";
import { DeploymentApi } from "./deployment.api";
const deploymentApiInjectable = getInjectable({
id: "deployment-api",
instantiate: (di) => {
assert(di.inject(createStoresAndApisInjectionToken), "deploymentApi is only available in certain environments");
return new DeploymentApi();
},
});
export default deploymentApiInjectable;

View File

@ -9,7 +9,6 @@ import type { DerivedKubeApiOptions } from "../kube-api";
import { KubeApi } from "../kube-api"; import { KubeApi } from "../kube-api";
import { metricsApi } from "./metrics.api"; import { metricsApi } from "./metrics.api";
import type { PodMetricData, PodSpec } from "./pod.api"; import type { PodMetricData, PodSpec } from "./pod.api";
import { isClusterPageContext } from "../../utils/cluster-id-url-parsing";
import type { KubeObjectScope, KubeObjectStatus, LabelSelector } from "../kube-object"; import type { KubeObjectScope, KubeObjectStatus, LabelSelector } from "../kube-object";
import { KubeObject } from "../kube-object"; import { KubeObject } from "../kube-object";
import { hasTypedProperty, isNumber, isObject } from "../../utils"; import { hasTypedProperty, isNumber, isObject } from "../../utils";
@ -166,7 +165,3 @@ export class Deployment extends KubeObject<DeploymentStatus, DeploymentSpec, Kub
return this.spec.replicas || 0; return this.spec.replicas || 0;
} }
} }
export const deploymentApi = isClusterPageContext()
? new DeploymentApi()
: undefined as never;

View File

@ -7,6 +7,7 @@ import { asLegacyGlobalForExtensionApi } from "../../../extensions/as-legacy-glo
import clusterRoleBindingApiInjectable from "./cluster-role-binding.api.injectable"; import clusterRoleBindingApiInjectable from "./cluster-role-binding.api.injectable";
import clusterRoleApiInjectable from "./cluster-role.api.injectable"; import clusterRoleApiInjectable from "./cluster-role.api.injectable";
import daemonSetApiInjectable from "./daemon-set.api.injectable"; import daemonSetApiInjectable from "./daemon-set.api.injectable";
import deploymentApiInjectable from "./deployment.api.injectable";
import podApiInjectable from "./pod.api.injectable"; import podApiInjectable from "./pod.api.injectable";
import replicaSetApiInjectable from "./replica-set.api.injectable"; import replicaSetApiInjectable from "./replica-set.api.injectable";
import roleApiInjectable from "./role.api.injectable"; import roleApiInjectable from "./role.api.injectable";
@ -52,3 +53,8 @@ export const replicaSetApi = asLegacyGlobalForExtensionApi(replicaSetApiInjectab
* @deprecated use `di.inject(statefulSetApiInjectable)` instead * @deprecated use `di.inject(statefulSetApiInjectable)` instead
*/ */
export const statefulSetApi = asLegacyGlobalForExtensionApi(statefulSetApiInjectable); export const statefulSetApi = asLegacyGlobalForExtensionApi(statefulSetApiInjectable);
/**
* @deprecated use `di.inject(deploymentApiInjectable)` instead
*/
export const deploymentApi = asLegacyGlobalForExtensionApi(deploymentApiInjectable);

View File

@ -11,6 +11,8 @@ import { merge } from "lodash";
import type { Response, RequestInit } from "node-fetch"; import type { Response, RequestInit } from "node-fetch";
import fetch from "node-fetch"; import fetch from "node-fetch";
import { stringify } from "querystring"; import { stringify } from "querystring";
import type { Patch } from "rfc6902";
import type { PartialDeep, ValueOf } from "type-fest";
import { EventEmitter } from "../../common/event-emitter"; import { EventEmitter } from "../../common/event-emitter";
import logger from "../../common/logger"; import logger from "../../common/logger";
import type { Defaulted } from "../utils"; import type { Defaulted } from "../utils";
@ -24,9 +26,8 @@ export interface JsonApiError {
errors?: { id: string; title: string; status?: number }[]; errors?: { id: string; title: string; status?: number }[];
} }
export interface JsonApiParams<D = any> { export interface JsonApiParams<D> {
query?: { [param: string]: string | number | any }; data?: PartialDeep<D>; // request body
data?: D; // request body
} }
export interface JsonApiLog { export interface JsonApiLog {
@ -49,7 +50,16 @@ export interface JsonApiConfig {
const httpAgent = new HttpAgent({ keepAlive: true }); const httpAgent = new HttpAgent({ keepAlive: true });
const httpsAgent = new HttpsAgent({ keepAlive: true }); const httpsAgent = new HttpsAgent({ keepAlive: true });
export class JsonApi<D = JsonApiData, P extends JsonApiParams = JsonApiParams> { export type QueryParam = string | number | boolean | null | undefined | readonly string[] | readonly number[] | readonly boolean[];
export type QueryParams = Partial<Record<string, QueryParam | undefined>>;
export type ParamsAndQuery<Params, Query> = (
ValueOf<Query> extends QueryParam
? Params & { query?: Query }
: Params & { query?: undefined }
);
export class JsonApi<Data = JsonApiData, Params extends JsonApiParams<Data> = JsonApiParams<Data>> {
static readonly reqInitDefault = { static readonly reqInitDefault = {
headers: { headers: {
"content-type": "application/json", "content-type": "application/json",
@ -68,15 +78,15 @@ export class JsonApi<D = JsonApiData, P extends JsonApiParams = JsonApiParams> {
this.getRequestOptions = config.getRequestOptions ?? (() => Promise.resolve({})); this.getRequestOptions = config.getRequestOptions ?? (() => Promise.resolve({}));
} }
public readonly onData = new EventEmitter<[D, Response]>(); public readonly onData = new EventEmitter<[Data, Response]>();
public readonly onError = new EventEmitter<[JsonApiErrorParsed, Response]>(); public readonly onError = new EventEmitter<[JsonApiErrorParsed, Response]>();
private readonly getRequestOptions: GetRequestOptions; private readonly getRequestOptions: GetRequestOptions;
get<T = D>(path: string, params?: P, reqInit: RequestInit = {}) { async getResponse<Query>(
return this.request<T>(path, params, { ...reqInit, method: "get" }); path: string,
} params?: ParamsAndQuery<Params, Query> | undefined,
init: RequestInit = {},
async getResponse(path: string, params?: P, init: RequestInit = {}): Promise<Response> { ): Promise<Response> {
let reqUrl = `${this.config.serverAddress}${this.config.apiBase}${path}`; let reqUrl = `${this.config.serverAddress}${this.config.apiBase}${path}`;
const reqInit = merge( const reqInit = merge(
{ {
@ -87,10 +97,10 @@ export class JsonApi<D = JsonApiData, P extends JsonApiParams = JsonApiParams> {
await this.getRequestOptions(), await this.getRequestOptions(),
init, init,
); );
const { query } = params || {} as P; const { query } = params ?? {};
if (query) { if (query) {
const queryString = stringify(query); const queryString = stringify(query as unknown as QueryParams);
reqUrl += (reqUrl.includes("?") ? "&" : "?") + queryString; reqUrl += (reqUrl.includes("?") ? "&" : "?") + queryString;
} }
@ -98,23 +108,51 @@ export class JsonApi<D = JsonApiData, P extends JsonApiParams = JsonApiParams> {
return fetch(reqUrl, reqInit); return fetch(reqUrl, reqInit);
} }
post<T = D>(path: string, params?: P, reqInit: RequestInit = {}) { get<OutData = Data, Query = QueryParams>(
return this.request<T>(path, params, { ...reqInit, method: "post" }); path: string,
params?: ParamsAndQuery<Params, Query> | undefined,
reqInit: RequestInit = {},
) {
return this.request<OutData, Query>(path, params, { ...reqInit, method: "get" });
} }
put<T = D>(path: string, params?: P, reqInit: RequestInit = {}) { post<OutData = Data, Query = QueryParams>(
return this.request<T>(path, params, { ...reqInit, method: "put" }); path: string,
params?: ParamsAndQuery<Params, Query> | undefined,
reqInit: RequestInit = {},
) {
return this.request<OutData, Query>(path, params, { ...reqInit, method: "post" });
} }
patch<T = D>(path: string, params?: P, reqInit: RequestInit = {}) { put<OutData = Data, Query = QueryParams>(
return this.request<T>(path, params, { ...reqInit, method: "PATCH" }); path: string,
params?: ParamsAndQuery<Params, Query> | undefined,
reqInit: RequestInit = {},
) {
return this.request<OutData, Query>(path, params, { ...reqInit, method: "put" });
} }
del<T = D>(path: string, params?: P, reqInit: RequestInit = {}) { patch<OutData = Data, Query = QueryParams>(
return this.request<T>(path, params, { ...reqInit, method: "delete" }); path: string,
params?: (ParamsAndQuery<Omit<Params, "data">, Query> & { data?: Patch | PartialDeep<Data> }) | undefined,
reqInit: RequestInit = {},
) {
return this.request<OutData, Query>(path, params, { ...reqInit, method: "patch" });
} }
protected async request<D>(path: string, params: P | undefined, init: Defaulted<RequestInit, "method">) { del<OutData = Data, Query = QueryParams>(
path: string,
params?: ParamsAndQuery<Params, Query> | undefined,
reqInit: RequestInit = {},
) {
return this.request<OutData, Query>(path, params, { ...reqInit, method: "delete" });
}
protected async request<OutData, Query = QueryParams>(
path: string,
params: (ParamsAndQuery<Omit<Params, "data">, Query> & { data?: unknown }) | undefined,
init: Defaulted<RequestInit, "method">,
) {
let reqUrl = `${this.config.serverAddress}${this.config.apiBase}${path}`; let reqUrl = `${this.config.serverAddress}${this.config.apiBase}${path}`;
const reqInit = merge( const reqInit = merge(
{}, {},
@ -122,14 +160,14 @@ export class JsonApi<D = JsonApiData, P extends JsonApiParams = JsonApiParams> {
await this.getRequestOptions(), await this.getRequestOptions(),
init, init,
); );
const { data, query } = params || {} as P; const { data, query } = params || {};
if (data && !reqInit.body) { if (data && !reqInit.body) {
reqInit.body = JSON.stringify(data); reqInit.body = JSON.stringify(data);
} }
if (query) { if (query) {
const queryString = stringify(query); const queryString = stringify(query as unknown as QueryParams);
reqUrl += (reqUrl.includes("?") ? "&" : "?") + queryString; reqUrl += (reqUrl.includes("?") ? "&" : "?") + queryString;
} }
@ -141,10 +179,10 @@ export class JsonApi<D = JsonApiData, P extends JsonApiParams = JsonApiParams> {
const res = await fetch(reqUrl, reqInit); const res = await fetch(reqUrl, reqInit);
return this.parseResponse<D>(res, infoLog); return this.parseResponse<OutData>(res, infoLog);
} }
protected async parseResponse<D>(res: Response, log: JsonApiLog): Promise<D> { protected async parseResponse<OutData>(res: Response, log: JsonApiLog): Promise<OutData> {
const { status } = res; const { status } = res;
const text = await res.text(); const text = await res.text();

View File

@ -8,11 +8,11 @@
import { splitArray } from "../utils"; import { splitArray } from "../utils";
export interface IKubeApiLinkRef { export interface IKubeApiLinkRef {
apiPrefix?: string; apiPrefix?: string | undefined;
apiVersion: string; apiVersion: string;
resource: string; resource: string;
name?: string; name?: string | undefined;
namespace?: string; namespace?: string | undefined;
} }
export interface IKubeApiParsed extends IKubeApiLinkRef { export interface IKubeApiParsed extends IKubeApiLinkRef {

View File

@ -105,19 +105,19 @@ export interface IgnoredKubeApiOptions {
apiBase?: any; apiBase?: any;
} }
export interface IKubeApiQueryParams { export interface KubeApiQueryParams {
watch?: boolean | number; watch?: boolean | number | undefined;
resourceVersion?: string; resourceVersion?: string | undefined;
timeoutSeconds?: number; timeoutSeconds?: number | undefined;
limit?: number; // doesn't work with ?watch limit?: number | undefined; // doesn't work with ?watch
continue?: string; // might be used with ?limit from second request continue?: string | undefined; // might be used with ?limit from second request
labelSelector?: string | string[]; // restrict list of objects by their labels, e.g. labelSelector: ["label=value"] labelSelector?: string | string[] | undefined; // restrict list of objects by their labels, e.g. labelSelector: ["label=value"]
fieldSelector?: string | string[]; // restrict list of objects by their fields, e.g. fieldSelector: "field=name" fieldSelector?: string | string[] | undefined; // restrict list of objects by their fields, e.g. fieldSelector: "field=name"
} }
export interface KubeApiListOptions { export interface KubeApiListOptions {
namespace?: string; namespace?: string | undefined;
reqInit?: RequestInit; reqInit?: RequestInit | undefined;
} }
export interface IKubePreferredVersion { export interface IKubePreferredVersion {
@ -126,15 +126,24 @@ export interface IKubePreferredVersion {
}; };
} }
export interface IKubeResourceList { export interface KubeApiResource {
resources: { categories?: string[];
kind: string; group?: string;
name: string; kind: string;
namespaced: boolean; name: string;
singularName: string; namespaced: boolean;
storageVersionHash: string; shortNames?: string[];
verbs: string[]; singularName: string;
}[]; storageVersionHash?: string;
verbs: string[];
version?: string;
}
export interface KubeApiResourceList {
apiVersion?: string;
groupVersion?: string;
kind?: string;
resources: KubeApiResource[];
} }
export interface ILocalKubeApiConfig { export interface ILocalKubeApiConfig {
@ -288,7 +297,7 @@ export interface ResourceDescriptor {
* *
* Note: if not provided and the resource kind is namespaced, then this defaults to `"default"` * Note: if not provided and the resource kind is namespaced, then this defaults to `"default"`
*/ */
namespace?: string; namespace?: string | undefined;
} }
export interface DeleteResourceDescriptor extends ResourceDescriptor { export interface DeleteResourceDescriptor extends ResourceDescriptor {
@ -320,7 +329,7 @@ export class KubeApi<
private watchId = 1; private watchId = 1;
protected readonly doCheckPreferredVersion: boolean; protected readonly doCheckPreferredVersion: boolean;
protected readonly fullApiPathname: string; protected readonly fullApiPathname: string;
protected readonly fallbackApiBases?: string[]; protected readonly fallbackApiBases: string[] | undefined;
protected readonly logger: Logger; protected readonly logger: Logger;
constructor({ constructor({
@ -382,7 +391,7 @@ export class KubeApi<
const { apiPrefix, apiGroup, apiVersionWithGroup, resource } = parseKubeApi(apiUrl); const { apiPrefix, apiGroup, apiVersionWithGroup, resource } = parseKubeApi(apiUrl);
// Request available resources // Request available resources
const { resources } = await this.request.get(`${apiPrefix}/${apiVersionWithGroup}`) as IKubeResourceList; const { resources } = (await this.request.get(`${apiPrefix}/${apiVersionWithGroup}`)) as unknown as KubeApiResourceList;
// If the resource is found in the group, use this apiUrl // If the resource is found in the group, use this apiUrl
if (resources.find(({ name }) => name === resource)) { if (resources.find(({ name }) => name === resource)) {
@ -461,7 +470,7 @@ export class KubeApi<
}); });
} }
getUrl({ name, namespace }: Partial<ResourceDescriptor> = {}, query?: Partial<IKubeApiQueryParams>) { getUrl({ name, namespace }: Partial<ResourceDescriptor> = {}, query?: Partial<KubeApiQueryParams>) {
const resourcePath = createKubeApiURL({ const resourcePath = createKubeApiURL({
apiPrefix: this.apiPrefix, apiPrefix: this.apiPrefix,
apiVersion: this.apiVersionWithGroup, apiVersion: this.apiVersionWithGroup,
@ -473,7 +482,7 @@ export class KubeApi<
return resourcePath + (query ? `?${stringify(this.normalizeQuery(query))}` : ""); return resourcePath + (query ? `?${stringify(this.normalizeQuery(query))}` : "");
} }
protected normalizeQuery(query: Partial<IKubeApiQueryParams> = {}) { protected normalizeQuery(query: Partial<KubeApiQueryParams> = {}) {
if (query.labelSelector) { if (query.labelSelector) {
query.labelSelector = [query.labelSelector].flat().join(","); query.labelSelector = [query.labelSelector].flat().join(",");
} }
@ -537,7 +546,7 @@ export class KubeApi<
return null; return null;
} }
private ensureMetadataSelfLink<T extends { selfLink?: string; namespace?: string; name: string }>(metadata: T): asserts metadata is T & { selfLink: string } { private ensureMetadataSelfLink<T extends { selfLink?: string | undefined; namespace?: string | undefined; name: string }>(metadata: T): asserts metadata is T & { selfLink: string } {
metadata.selfLink ||= createKubeApiURL({ metadata.selfLink ||= createKubeApiURL({
apiPrefix: this.apiPrefix, apiPrefix: this.apiPrefix,
apiVersion: this.apiVersionWithGroup, apiVersion: this.apiVersionWithGroup,
@ -547,7 +556,7 @@ export class KubeApi<
}); });
} }
async list({ namespace = "", reqInit }: KubeApiListOptions = {}, query?: IKubeApiQueryParams): Promise<K[] | null> { async list({ namespace = "", reqInit }: KubeApiListOptions = {}, query?: KubeApiQueryParams): Promise<K[] | null> {
await this.checkPreferredVersion(); await this.checkPreferredVersion();
const url = this.getUrl({ namespace }); const url = this.getUrl({ namespace });
@ -565,7 +574,7 @@ export class KubeApi<
throw new Error(`GET multiple request to ${url} returned not an array: ${JSON.stringify(parsed)}`); throw new Error(`GET multiple request to ${url} returned not an array: ${JSON.stringify(parsed)}`);
} }
async get(desc: ResourceDescriptor, query?: IKubeApiQueryParams): Promise<K | null> { async get(desc: ResourceDescriptor, query?: KubeApiQueryParams): Promise<K | null> {
await this.checkPreferredVersion(); await this.checkPreferredVersion();
const url = this.getUrl(desc); const url = this.getUrl(desc);
@ -623,7 +632,7 @@ export class KubeApi<
return parsed; return parsed;
} }
async patch(desc: ResourceDescriptor, data?: PartialDeep<K> | Patch, strategy: KubeApiPatchType = "strategic"): Promise<K | null> { async patch(desc: ResourceDescriptor, data: PartialDeep<K> | Patch, strategy: KubeApiPatchType = "strategic"): Promise<K | null> {
await this.checkPreferredVersion(); await this.checkPreferredVersion();
const apiUrl = this.getUrl(desc); const apiUrl = this.getUrl(desc);
@ -652,7 +661,7 @@ export class KubeApi<
}); });
} }
getWatchUrl(namespace = "", query: IKubeApiQueryParams = {}) { getWatchUrl(namespace = "", query: KubeApiQueryParams = {}) {
return this.getUrl({ namespace }, { return this.getUrl({ namespace }, {
watch: 1, watch: 1,
resourceVersion: this.getResourceVersion(namespace), resourceVersion: this.getResourceVersion(namespace),

View File

@ -61,7 +61,11 @@ export class KubeJsonApi extends JsonApi<KubeJsonApiData> {
}); });
} }
protected parseError(error: KubeJsonApiError | any, res: Response): string[] { protected parseError(error: KubeJsonApiError | string, res: Response): string[] {
if (typeof error === "string") {
return [error];
}
const { status, reason, message } = error; const { status, reason, message } = error;
if (status && reason) { if (status && reason) {

View File

@ -12,7 +12,7 @@ import type { KubeJsonApiDataFor, KubeObject } from "./kube-object";
import { KubeStatus } from "./kube-object"; import { KubeStatus } from "./kube-object";
import type { IKubeWatchEvent } from "./kube-watch-event"; import type { IKubeWatchEvent } from "./kube-watch-event";
import { ItemStore } from "../item.store"; import { ItemStore } from "../item.store";
import type { IKubeApiQueryParams, KubeApi, KubeApiWatchCallback } from "./kube-api"; import type { KubeApiQueryParams, KubeApi, KubeApiWatchCallback } from "./kube-api";
import { parseKubeApi } from "./kube-api-parse"; import { parseKubeApi } from "./kube-api-parse";
import type { RequestInit } from "node-fetch"; import type { RequestInit } from "node-fetch";
import AbortController from "abort-controller"; import AbortController from "abort-controller";
@ -20,16 +20,19 @@ import type { Patch } from "rfc6902";
import logger from "../logger"; import logger from "../logger";
import assert from "assert"; import assert from "assert";
import type { PartialDeep } from "type-fest"; import type { PartialDeep } from "type-fest";
import { entries } from "../utils/objects";
export type OnLoadFailure = (error: unknown) => void;
export interface KubeObjectStoreLoadingParams { export interface KubeObjectStoreLoadingParams {
namespaces: string[]; namespaces: string[];
reqInit?: RequestInit; reqInit?: RequestInit | undefined;
/** /**
* A function that is called when listing fails. If set then blocks errors * A function that is called when listing fails. If set then blocks errors
* being rejected with * being rejected with
*/ */
onLoadFailure?: (err: any) => void; onLoadFailure?: OnLoadFailure | undefined;
} }
export interface KubeObjectStoreLoadAllParams { export interface KubeObjectStoreLoadAllParams {
@ -41,7 +44,7 @@ export interface KubeObjectStoreLoadAllParams {
* A function that is called when listing fails. If set then blocks errors * A function that is called when listing fails. If set then blocks errors
* being rejected with * being rejected with
*/ */
onLoadFailure?: (err: any) => void; onLoadFailure?: OnLoadFailure | undefined;
} }
export interface KubeObjectStoreSubscribeParams { export interface KubeObjectStoreSubscribeParams {
@ -49,7 +52,7 @@ export interface KubeObjectStoreSubscribeParams {
* A function that is called when listing fails. If set then blocks errors * A function that is called when listing fails. If set then blocks errors
* being rejected with * being rejected with
*/ */
onLoadFailure?: (err: any) => void; onLoadFailure?: OnLoadFailure | undefined;
/** /**
* An optional parent abort controller * An optional parent abort controller
@ -88,7 +91,7 @@ export abstract class KubeObjectStore<
static readonly defaultContext = observable.box<ClusterContext>(); // TODO: support multiple cluster contexts static readonly defaultContext = observable.box<ClusterContext>(); // TODO: support multiple cluster contexts
public readonly api: A; public readonly api: A;
public readonly limit?: number; public readonly limit: number | undefined;
public readonly bufferSize: number; public readonly bufferSize: number;
@observable private loadedNamespaces: string[] | undefined = undefined; @observable private loadedNamespaces: string[] | undefined = undefined;
@ -130,7 +133,7 @@ export abstract class KubeObjectStore<
return this.contextItems.length; return this.contextItems.length;
} }
get query(): IKubeApiQueryParams { get query(): KubeApiQueryParams {
const { limit } = this; const { limit } = this;
if (!limit) { if (!limit) {
@ -170,7 +173,7 @@ export abstract class KubeObjectStore<
return this.items.find(item => item.selfLink === path); return this.items.find(item => item.selfLink === path);
} }
getByLabel(labels: string[] | { [label: string]: string }): K[] { getByLabel(labels: string[] | Partial<Record<string, string>>): K[] {
if (Array.isArray(labels)) { if (Array.isArray(labels)) {
return this.items.filter((item: K) => { return this.items.filter((item: K) => {
const itemLabels = item.getLabels(); const itemLabels = item.getLabels();
@ -181,7 +184,7 @@ export abstract class KubeObjectStore<
return this.items.filter((item: K) => { return this.items.filter((item: K) => {
const itemLabels = item.metadata.labels || {}; const itemLabels = item.metadata.labels || {};
return Object.entries(labels) return entries(labels)
.every(([key, value]) => itemLabels[key] === value); .every(([key, value]) => itemLabels[key] === value);
}); });
} }

View File

@ -10,7 +10,6 @@ import type { KubeJsonApiData, KubeJsonApiDataList, KubeJsonApiListMetadata } fr
import { autoBind, formatDuration, hasOptionalTypedProperty, hasTypedProperty, isObject, isString, isNumber, bindPredicate, isTypedArray, isRecord, json } from "../utils"; import { autoBind, formatDuration, hasOptionalTypedProperty, hasTypedProperty, isObject, isString, isNumber, bindPredicate, isTypedArray, isRecord, json } from "../utils";
import type { ItemObject } from "../item.store"; import type { ItemObject } from "../item.store";
import { apiKube } from "./index"; import { apiKube } from "./index";
import type { JsonApiParams } from "./json-api";
import * as resourceApplierApi from "./endpoints/resource-applier.api"; import * as resourceApplierApi from "./endpoints/resource-applier.api";
import type { Patch } from "rfc6902"; import type { Patch } from "rfc6902";
import assert from "assert"; import assert from "assert";
@ -653,7 +652,7 @@ export class KubeObject<
/** /**
* @deprecated use KubeApi.delete instead * @deprecated use KubeApi.delete instead
*/ */
delete(params?: JsonApiParams) { delete(params?: object) {
assert(this.selfLink, "selfLink must be present to delete self"); assert(this.selfLink, "selfLink must be present to delete self");
return apiKube.del(this.selfLink, params); return apiKube.del(this.selfLink, params);

View File

@ -11,8 +11,8 @@ export function fromEntries<T, Key extends string>(entries: Iterable<readonly [K
return Object.fromEntries(entries) as Record<Key, T>; return Object.fromEntries(entries) as Record<Key, T>;
} }
export function entries<K extends string | number | symbol, V>(obj: Partial<Record<K, V>> | null | undefined): [K, V][];
export function entries<K extends string | number | symbol, V>(obj: Record<K, V> | null | undefined): [K, V][]; export function entries<K extends string | number | symbol, V>(obj: Record<K, V> | null | undefined): [K, V][];
export function entries<K extends string | number | symbol, V>(obj: Partial<Record<K, V>> | null | undefined): [K, V | undefined][];
export function entries<K extends string | number | symbol, V>(obj: Record<K, V> | null | undefined): [K, V][] { export function entries<K extends string | number | symbol, V>(obj: Record<K, V> | null | undefined): [K, V][] {
if (obj && typeof obj == "object") { if (obj && typeof obj == "object") {

View File

@ -3,10 +3,14 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
export type RemoveUndefinedFromValues<K> = {
[P in keyof K]: NonNullable<K[P]>;
};
/** /**
* This type helps define which fields of some type will always be defined * This type helps define which fields of some type will always be defined
*/ */
export type Defaulted<Params, DefaultParams extends keyof Params> = Required<Pick<Params, DefaultParams>> & Omit<Params, DefaultParams>; export type Defaulted<Params, DefaultParams extends keyof Params> = RemoveUndefinedFromValues<Required<Pick<Params, DefaultParams>>> & Omit<Params, DefaultParams>;
export type OptionVarient<Key, Base, RequiredKey extends keyof Base> = { export type OptionVarient<Key, Base, RequiredKey extends keyof Base> = {
type: Key; type: Key;

View File

@ -90,7 +90,7 @@ export type { KubeObjectStoreLoadAllParams, KubeObjectStoreLoadingParams, KubeOb
export type { EventStore } from "../../renderer/components/+events/event.store"; export type { EventStore } from "../../renderer/components/+events/event.store";
export type { PodStore as PodsStore } from "../../renderer/components/+workloads-pods/store"; export type { PodStore as PodsStore } from "../../renderer/components/+workloads-pods/store";
export type { NodesStore } from "../../renderer/components/+nodes/nodes.store"; export type { NodesStore } from "../../renderer/components/+nodes/nodes.store";
export type { DeploymentStore } from "../../renderer/components/+workloads-deployments/deployments.store"; export type { DeploymentStore } from "../../renderer/components/+workloads-deployments/store";
export type { DaemonSetStore } from "../../renderer/components/+workloads-daemonsets/store"; export type { DaemonSetStore } from "../../renderer/components/+workloads-daemonsets/store";
export type { StatefulSetStore } from "../../renderer/components/+workloads-statefulsets/statefulset.store"; export type { StatefulSetStore } from "../../renderer/components/+workloads-statefulsets/statefulset.store";
export type { JobStore } from "../../renderer/components/+workloads-jobs/job.store"; export type { JobStore } from "../../renderer/components/+workloads-jobs/job.store";

View File

@ -11,7 +11,7 @@ import {
MetricType, MetricType,
} from "./cluster-overview-store"; } from "./cluster-overview-store";
import createStorageInjectable from "../../../utils/create-storage/create-storage.injectable"; import createStorageInjectable from "../../../utils/create-storage/create-storage.injectable";
import apiManagerInjectable from "../../kube-object-menu/dependencies/api-manager.injectable"; import apiManagerInjectable from "../../../../common/k8s-api/api-manager/manager.injectable";
import { clusterApi } from "../../../../common/k8s-api/endpoints"; import { clusterApi } from "../../../../common/k8s-api/endpoints";
const clusterOverviewStoreInjectable = getInjectable({ const clusterOverviewStoreInjectable = getInjectable({

View File

@ -5,7 +5,7 @@
import { computed, reaction, makeObservable } from "mobx"; import { computed, reaction, makeObservable } from "mobx";
import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store"; import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store";
import { autoBind } from "../../utils"; import { autoBind, isClusterPageContext } from "../../utils";
import type { CustomResourceDefinition, CustomResourceDefinitionApi } from "../../../common/k8s-api/endpoints/crd.api"; import type { CustomResourceDefinition, CustomResourceDefinitionApi } from "../../../common/k8s-api/endpoints/crd.api";
import { crdApi } from "../../../common/k8s-api/endpoints/crd.api"; import { crdApi } from "../../../common/k8s-api/endpoints/crd.api";
import { apiManager } from "../../../common/k8s-api/api-manager"; import { apiManager } from "../../../common/k8s-api/api-manager";
@ -70,6 +70,10 @@ export class CRDStore extends KubeObjectStore<CustomResourceDefinition, CustomRe
} }
} }
export const crdStore = new CRDStore(); export const crdStore = isClusterPageContext()
? new CRDStore()
: undefined as never;
apiManager.registerStore(crdStore); if (isClusterPageContext()) {
apiManager.registerStore(crdStore);
}

View File

@ -4,7 +4,7 @@
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { NamespaceStore } from "./namespace.store"; import { NamespaceStore } from "./namespace.store";
import apiManagerInjectable from "../../kube-object-menu/dependencies/api-manager.injectable"; import apiManagerInjectable from "../../../../common/k8s-api/api-manager/manager.injectable";
import createStorageInjectable from "../../../utils/create-storage/create-storage.injectable"; import createStorageInjectable from "../../../utils/create-storage/create-storage.injectable";
import { namespaceApi } from "../../../../common/k8s-api/endpoints"; import { namespaceApi } from "../../../../common/k8s-api/endpoints";

View File

@ -7,9 +7,15 @@ import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store";
import type { Endpoints, EndpointsApi, EndpointsData } from "../../../common/k8s-api/endpoints/endpoint.api"; import type { Endpoints, EndpointsApi, EndpointsData } from "../../../common/k8s-api/endpoints/endpoint.api";
import { endpointsApi } from "../../../common/k8s-api/endpoints/endpoint.api"; import { endpointsApi } from "../../../common/k8s-api/endpoints/endpoint.api";
import { apiManager } from "../../../common/k8s-api/api-manager"; import { apiManager } from "../../../common/k8s-api/api-manager";
import { isClusterPageContext } from "../../utils";
export class EndpointStore extends KubeObjectStore<Endpoints, EndpointsApi, EndpointsData> { export class EndpointStore extends KubeObjectStore<Endpoints, EndpointsApi, EndpointsData> {
} }
export const endpointStore = new EndpointStore(endpointsApi); export const endpointStore = isClusterPageContext()
apiManager.registerStore(endpointStore); ? new EndpointStore(endpointsApi)
: undefined as never;
if (isClusterPageContext()) {
apiManager.registerStore(endpointStore);
}

View File

@ -9,7 +9,7 @@ import { apiManager } from "../../../common/k8s-api/api-manager";
import type { Node, NodeApi } from "../../../common/k8s-api/endpoints"; import type { Node, NodeApi } from "../../../common/k8s-api/endpoints";
import { nodeApi } from "../../../common/k8s-api/endpoints"; import { nodeApi } from "../../../common/k8s-api/endpoints";
import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store"; import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store";
import { autoBind } from "../../utils"; import { autoBind, isClusterPageContext } from "../../utils";
export class NodesStore extends KubeObjectStore<Node, NodeApi> { export class NodesStore extends KubeObjectStore<Node, NodeApi> {
constructor() { constructor() {
@ -32,5 +32,10 @@ export class NodesStore extends KubeObjectStore<Node, NodeApi> {
} }
} }
export const nodesStore = new NodesStore(); export const nodesStore = isClusterPageContext()
apiManager.registerStore(nodesStore); ? new NodesStore()
: undefined as never;
if (isClusterPageContext()) {
apiManager.registerStore(nodesStore);
}

View File

@ -6,7 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert"; import assert from "assert";
import { createStoresAndApisInjectionToken } from "../../../../common/k8s-api/create-stores-apis.token"; import { createStoresAndApisInjectionToken } from "../../../../common/k8s-api/create-stores-apis.token";
import clusterRoleBindingApiInjectable from "../../../../common/k8s-api/endpoints/cluster-role-binding.api.injectable"; import clusterRoleBindingApiInjectable from "../../../../common/k8s-api/endpoints/cluster-role-binding.api.injectable";
import apiManagerInjectable from "../../kube-object-menu/dependencies/api-manager.injectable"; import apiManagerInjectable from "../../../../common/k8s-api/api-manager/manager.injectable";
import { ClusterRoleBindingStore } from "./store"; import { ClusterRoleBindingStore } from "./store";
const clusterRoleBindingStoreInjectable = getInjectable({ const clusterRoleBindingStoreInjectable = getInjectable({

View File

@ -6,7 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert"; import assert from "assert";
import { createStoresAndApisInjectionToken } from "../../../../common/k8s-api/create-stores-apis.token"; import { createStoresAndApisInjectionToken } from "../../../../common/k8s-api/create-stores-apis.token";
import clusterRoleApiInjectable from "../../../../common/k8s-api/endpoints/cluster-role.api.injectable"; import clusterRoleApiInjectable from "../../../../common/k8s-api/endpoints/cluster-role.api.injectable";
import apiManagerInjectable from "../../kube-object-menu/dependencies/api-manager.injectable"; import apiManagerInjectable from "../../../../common/k8s-api/api-manager/manager.injectable";
import { ClusterRolesStore } from "./store"; import { ClusterRolesStore } from "./store";
const clusterRoleStoreInjectable = getInjectable({ const clusterRoleStoreInjectable = getInjectable({

View File

@ -6,7 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert"; import assert from "assert";
import roleApiInjectable from "../../../../common/k8s-api/endpoints/role.api.injectable"; import roleApiInjectable from "../../../../common/k8s-api/endpoints/role.api.injectable";
import createStoresAndApisInjectable from "../../../create-stores-apis.injectable"; import createStoresAndApisInjectable from "../../../create-stores-apis.injectable";
import apiManagerInjectable from "../../kube-object-menu/dependencies/api-manager.injectable"; import apiManagerInjectable from "../../../../common/k8s-api/api-manager/manager.injectable";
import { RoleStore } from "./store"; import { RoleStore } from "./store";
const roleStoreInjectable = getInjectable({ const roleStoreInjectable = getInjectable({

View File

@ -6,7 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert"; import assert from "assert";
import serviceAccountApiInjectable from "../../../../common/k8s-api/endpoints/service-account.api.injectable"; import serviceAccountApiInjectable from "../../../../common/k8s-api/endpoints/service-account.api.injectable";
import createStoresAndApisInjectable from "../../../create-stores-apis.injectable"; import createStoresAndApisInjectable from "../../../create-stores-apis.injectable";
import apiManagerInjectable from "../../kube-object-menu/dependencies/api-manager.injectable"; import apiManagerInjectable from "../../../../common/k8s-api/api-manager/manager.injectable";
import { ServiceAccountStore } from "./store"; import { ServiceAccountStore } from "./store";
const serviceAccountStoreInjectable = getInjectable({ const serviceAccountStoreInjectable = getInjectable({

View File

@ -7,7 +7,7 @@ import assert from "assert";
import getPodsByOwnerIdInjectable from "../+workloads-pods/get-pods-by-owner-id.injectable"; import getPodsByOwnerIdInjectable from "../+workloads-pods/get-pods-by-owner-id.injectable";
import daemonSetApiInjectable from "../../../common/k8s-api/endpoints/daemon-set.api.injectable"; import daemonSetApiInjectable from "../../../common/k8s-api/endpoints/daemon-set.api.injectable";
import createStoresAndApisInjectable from "../../create-stores-apis.injectable"; import createStoresAndApisInjectable from "../../create-stores-apis.injectable";
import apiManagerInjectable from "../kube-object-menu/dependencies/api-manager.injectable"; import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
import { DaemonSetStore } from "./store"; import { DaemonSetStore } from "./store";
const daemonSetStoreInjectable = getInjectable({ const daemonSetStoreInjectable = getInjectable({

View File

@ -16,7 +16,7 @@ import { PodDetailsTolerations } from "../+workloads-pods/pod-details-toleration
import { PodDetailsAffinities } from "../+workloads-pods/pod-details-affinities"; import { PodDetailsAffinities } from "../+workloads-pods/pod-details-affinities";
import type { KubeObjectDetailsProps } from "../kube-object-details"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import { ResourceMetrics, ResourceMetricsText } from "../resource-metrics"; import { ResourceMetrics, ResourceMetricsText } from "../resource-metrics";
import { deploymentStore } from "./deployments.store"; import type { DeploymentStore } from "./store";
import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts"; import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts";
import { makeObservable, observable, reaction } from "mobx"; import { makeObservable, observable, reaction } from "mobx";
import { PodDetailsList } from "../+workloads-pods/pod-details-list"; import { PodDetailsList } from "../+workloads-pods/pod-details-list";
@ -32,6 +32,7 @@ import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.inj
import type { PodStore } from "../+workloads-pods/store"; import type { PodStore } from "../+workloads-pods/store";
import podStoreInjectable from "../+workloads-pods/store.injectable"; import podStoreInjectable from "../+workloads-pods/store.injectable";
import replicaSetStoreInjectable from "../+workloads-replicasets/store.injectable"; import replicaSetStoreInjectable from "../+workloads-replicasets/store.injectable";
import deploymentStoreInjectable from "./store.injectable";
export interface DeploymentDetailsProps extends KubeObjectDetailsProps<Deployment> { export interface DeploymentDetailsProps extends KubeObjectDetailsProps<Deployment> {
} }
@ -40,6 +41,7 @@ interface Dependencies {
subscribeStores: SubscribeStores; subscribeStores: SubscribeStores;
podStore: PodStore; podStore: PodStore;
replicaSetStore: ReplicaSetStore; replicaSetStore: ReplicaSetStore;
deploymentStore: DeploymentStore;
} }
@observer @observer
@ -71,7 +73,7 @@ class NonInjectedDeploymentDetails extends React.Component<DeploymentDetailsProp
}; };
render() { render() {
const { object: deployment, podStore, replicaSetStore } = this.props; const { object: deployment, podStore, replicaSetStore, deploymentStore } = this.props;
if (!deployment) { if (!deployment) {
return null; return null;
@ -171,6 +173,7 @@ export const DeploymentDetails = withInjectables<Dependencies, DeploymentDetails
subscribeStores: di.inject(subscribeStoresInjectable), subscribeStores: di.inject(subscribeStoresInjectable),
podStore: di.inject(podStoreInjectable), podStore: di.inject(podStoreInjectable),
replicaSetStore: di.inject(replicaSetStoreInjectable), replicaSetStore: di.inject(replicaSetStoreInjectable),
deploymentStore: di.inject(deploymentStoreInjectable),
}), }),
}); });

View File

@ -1,14 +0,0 @@
/**
* 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 { deploymentStore } from "./deployments.store";
const deploymentsStoreInjectable = getInjectable({
id: "deployments-store",
instantiate: () => deploymentStore,
causesSideEffects: true,
});
export default deploymentsStoreInjectable;

View File

@ -1,53 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { podStore } from "../+workloads-pods/legacy-store";
import { apiManager } from "../../../common/k8s-api/api-manager";
import type { Deployment, DeploymentApi } from "../../../common/k8s-api/endpoints";
import { deploymentApi, PodStatusPhase } from "../../../common/k8s-api/endpoints";
import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store";
import { isClusterPageContext } from "../../utils";
export class DeploymentStore extends KubeObjectStore<Deployment, DeploymentApi> {
protected sortItems(items: Deployment[]) {
return super.sortItems(items, [
item => item.getReplicas(),
], "desc");
}
getStatuses(deployments?: Deployment[]) {
const status = { running: 0, failed: 0, pending: 0 };
deployments?.forEach(deployment => {
const pods = this.getChildPods(deployment);
if (pods.some(pod => pod.getStatus() === PodStatusPhase.FAILED)) {
status.failed++;
}
else if (pods.some(pod => pod.getStatus() === PodStatusPhase.PENDING)) {
status.pending++;
}
else {
status.running++;
}
});
return status;
}
getChildPods(deployment: Deployment) {
return podStore
.getByLabel(deployment.getTemplateLabels())
.filter(pod => pod.getNs() === deployment.getNs());
}
}
export const deploymentStore = isClusterPageContext()
? new DeploymentStore(deploymentApi)
: undefined as never;
if (isClusterPageContext()) {
apiManager.registerStore(deploymentStore);
}

View File

@ -8,7 +8,7 @@ import "./deployments.scss";
import React from "react"; import React from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import type { Deployment } from "../../../common/k8s-api/endpoints"; import type { Deployment } from "../../../common/k8s-api/endpoints";
import { deploymentStore } from "./deployments.store"; import { deploymentStore } from "./legacy-store";
import { eventStore } from "../+events/event.store"; import { eventStore } from "../+events/event.store";
import { KubeObjectListLayout } from "../kube-object-list-layout"; import { KubeObjectListLayout } from "../kube-object-list-layout";
import { cssNames } from "../../utils"; import { cssNames } from "../../utils";

View File

@ -0,0 +1,12 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { asLegacyGlobalForExtensionApi } from "../../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
import deploymentStoreInjectable from "./store.injectable";
/**
* @deprecated use `di.inject(deploymentStoreInjectable)` instead
*/
export const deploymentStore = asLegacyGlobalForExtensionApi(deploymentStoreInjectable);

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 assert from "assert";
import podStoreInjectable from "../+workloads-pods/store.injectable";
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
import { createStoresAndApisInjectionToken } from "../../../common/k8s-api/create-stores-apis.token";
import deploymentApiInjectable from "../../../common/k8s-api/endpoints/deployment.api.injectable";
import { DeploymentStore } from "./store";
const deploymentStoreInjectable = getInjectable({
id: "deployment-store",
instantiate: (di) => {
assert(di.inject(createStoresAndApisInjectionToken), "deploymentStore is only available in certain environments");
const api = di.inject(deploymentApiInjectable);
const apiManager = di.inject(apiManagerInjectable);
const store = new DeploymentStore({
podStore: di.inject(podStoreInjectable),
}, api);
apiManager.registerStore(store);
return store;
},
});
export default deploymentStoreInjectable;

View File

@ -0,0 +1,63 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { PodStore } from "../+workloads-pods/store";
import type { Deployment, DeploymentApi } from "../../../common/k8s-api/endpoints";
import { PodStatusPhase } from "../../../common/k8s-api/endpoints";
import type { KubeObjectStoreOptions } from "../../../common/k8s-api/kube-object.store";
import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store";
// This needs to be disables because of https://github.com/microsoft/TypeScript/issues/15300
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
export type DeploymentStatuses = {
running: number;
failed: number;
pending: number;
};
export interface DeploymentStoreDependencies {
readonly podStore: PodStore;
}
export class DeploymentStore extends KubeObjectStore<Deployment, DeploymentApi> {
constructor(protected readonly dependencies: DeploymentStoreDependencies, api: DeploymentApi, opts?: KubeObjectStoreOptions) {
super(api, opts);
}
protected sortItems(items: Deployment[]) {
return super.sortItems(items, [
item => item.getReplicas(),
], "desc");
}
getStatuses(deployments: Deployment[]): DeploymentStatuses;
/**
* @deprecated
*/
getStatuses(deployments: Deployment[] | undefined): DeploymentStatuses;
getStatuses(deployments: Deployment[] = []) {
const status = { running: 0, failed: 0, pending: 0 };
deployments.forEach(deployment => {
const statuses = new Set(this.getChildPods(deployment).map(pod => pod.getStatus()));
if (statuses.has(PodStatusPhase.FAILED)) {
status.failed++;
} else if (statuses.has(PodStatusPhase.PENDING)) {
status.pending++;
} else {
status.running++;
}
});
return status;
}
getChildPods(deployment: Deployment) {
return this.dependencies.podStore
.getByLabel(deployment.getTemplateLabels())
.filter(pod => pod.getNs() === deployment.getNs());
}
}

View File

@ -8,7 +8,7 @@ import "./overview.scss";
import React from "react"; import React from "react";
import { disposeOnUnmount, observer } from "mobx-react"; import { disposeOnUnmount, observer } from "mobx-react";
import { eventStore } from "../+events/event.store"; import { eventStore } from "../+events/event.store";
import { deploymentStore } from "../+workloads-deployments/deployments.store"; import { deploymentStore } from "../+workloads-deployments/store";
import { statefulSetStore } from "../+workloads-statefulsets/statefulset.store"; import { statefulSetStore } from "../+workloads-statefulsets/statefulset.store";
import { jobStore } from "../+workloads-jobs/job.store"; import { jobStore } from "../+workloads-jobs/job.store";
import { cronJobStore } from "../+workloads-cronjobs/cronjob.store"; import { cronJobStore } from "../+workloads-cronjobs/cronjob.store";

View File

@ -6,7 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import { workloadInjectionToken } from "../workload-injection-token"; import { workloadInjectionToken } from "../workload-injection-token";
import { ResourceNames } from "../../../../utils/rbac"; import { ResourceNames } from "../../../../utils/rbac";
import namespaceStoreInjectable from "../../../+namespaces/namespace-store/namespace-store.injectable"; import namespaceStoreInjectable from "../../../+namespaces/namespace-store/namespace-store.injectable";
import deploymentsStoreInjectable from "../../../+workloads-deployments/deployments-store.injectable"; import deploymentsStoreInjectable from "../../../+workloads-deployments/store.injectable";
import navigateToDeploymentsInjectable from "../../../../../common/front-end-routing/routes/cluster/workloads/deployments/navigate-to-deployments.injectable"; import navigateToDeploymentsInjectable from "../../../../../common/front-end-routing/routes/cluster/workloads/deployments/navigate-to-deployments.injectable";
import { computed } from "mobx"; import { computed } from "mobx";

View File

@ -9,7 +9,7 @@ export interface Workload {
resourceName: string; resourceName: string;
open: () => void; open: () => void;
amountOfItems: IComputedValue<number>; amountOfItems: IComputedValue<number>;
status: IComputedValue<Record<string, number>>; status: IComputedValue<Partial<Record<string, number>>>;
title: string; title: string;
orderNumber: number; orderNumber: number;
} }

View File

@ -5,7 +5,7 @@
import React from "react"; import React from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import type { PodVolumeVariants, Pod, SecretReference } from "../../../../../common/k8s-api/endpoints"; import type { PodVolumeVariants, Pod, SecretReference } from "../../../../../common/k8s-api/endpoints";
import type { IKubeApiQueryParams, ResourceDescriptor } from "../../../../../common/k8s-api/kube-api"; import type { KubeApiQueryParams, ResourceDescriptor } from "../../../../../common/k8s-api/kube-api";
import type { LocalObjectReference } from "../../../../../common/k8s-api/kube-object"; import type { LocalObjectReference } from "../../../../../common/k8s-api/kube-object";
import { DrawerItem } from "../../../drawer"; import { DrawerItem } from "../../../drawer";
import { getDetailsUrl } from "../../../kube-detail-params"; import { getDetailsUrl } from "../../../kube-detail-params";
@ -19,7 +19,7 @@ export interface PodVolumeVariantSpecificProps<Kind extends keyof PodVolumeVaria
export type VolumeVariantComponent<Kind extends keyof PodVolumeVariants> = React.FunctionComponent<PodVolumeVariantSpecificProps<Kind>>; export type VolumeVariantComponent<Kind extends keyof PodVolumeVariants> = React.FunctionComponent<PodVolumeVariantSpecificProps<Kind>>;
export interface LocalRefPropsApi { export interface LocalRefPropsApi {
getUrl(desc?: Partial<ResourceDescriptor>, query?: Partial<IKubeApiQueryParams>): string; getUrl(desc?: Partial<ResourceDescriptor>, query?: Partial<KubeApiQueryParams>): string;
} }
export interface LocalRefProps { export interface LocalRefProps {

View File

@ -6,7 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert"; import assert from "assert";
import podApiInjectable from "../../../common/k8s-api/endpoints/pod.api.injectable"; import podApiInjectable from "../../../common/k8s-api/endpoints/pod.api.injectable";
import createStoresAndApisInjectable from "../../create-stores-apis.injectable"; import createStoresAndApisInjectable from "../../create-stores-apis.injectable";
import apiManagerInjectable from "../kube-object-menu/dependencies/api-manager.injectable"; import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
import { PodStore } from "./store"; import { PodStore } from "./store";
const podStoreInjectable = getInjectable({ const podStoreInjectable = getInjectable({

View File

@ -7,7 +7,7 @@ import assert from "assert";
import getPodsByOwnerIdInjectable from "../+workloads-pods/get-pods-by-owner-id.injectable"; import getPodsByOwnerIdInjectable from "../+workloads-pods/get-pods-by-owner-id.injectable";
import replicaSetApiInjectable from "../../../common/k8s-api/endpoints/replica-set.api.injectable"; import replicaSetApiInjectable from "../../../common/k8s-api/endpoints/replica-set.api.injectable";
import createStoresAndApisInjectable from "../../create-stores-apis.injectable"; import createStoresAndApisInjectable from "../../create-stores-apis.injectable";
import apiManagerInjectable from "../kube-object-menu/dependencies/api-manager.injectable"; import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
import { ReplicaSetStore } from "./store"; import { ReplicaSetStore } from "./store";
const replicaSetStoreInjectable = getInjectable({ const replicaSetStoreInjectable = getInjectable({

View File

@ -4,10 +4,13 @@
*/ */
import { observable } from "mobx"; import { observable } from "mobx";
import { deploymentStore } from "../+workloads-deployments/deployments.store"; import type { DeploymentStore } from "../+workloads-deployments/store";
import { podStore } from "../+workloads-pods/legacy-store"; import deploymentStoreInjectable from "../+workloads-deployments/store.injectable";
import podStoreInjectable from "../+workloads-pods/store.injectable";
import type { PodSpec } from "../../../common/k8s-api/endpoints"; import type { PodSpec } from "../../../common/k8s-api/endpoints";
import { Deployment, Pod } from "../../../common/k8s-api/endpoints"; import { Deployment, Pod } from "../../../common/k8s-api/endpoints";
import createStoresAndApisInjectable from "../../create-stores-apis.injectable";
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
const spec: PodSpec = { const spec: PodSpec = {
containers: [{ containers: [{
@ -200,13 +203,22 @@ const failedPod = new Pod({
}); });
describe("Deployment Store tests", () => { describe("Deployment Store tests", () => {
beforeAll(() => { let deploymentStore: DeploymentStore;
beforeEach(() => {
const di = getDiForUnitTesting({ doGeneralOverrides: true });
di.override(createStoresAndApisInjectable, () => true);
const podStore = di.inject(podStoreInjectable);
// Add pods to pod store // Add pods to pod store
podStore.items = observable.array([ podStore.items = observable.array([
runningPod, runningPod,
failedPod, failedPod,
pendingPod, pendingPod,
]); ]);
deploymentStore = di.inject(deploymentStoreInjectable);
}); });
it("gets Deployment statuses in proper sorting order", () => { it("gets Deployment statuses in proper sorting order", () => {

View File

@ -19,7 +19,7 @@ import type { DiRender } from "../test-utils/renderFor";
import { renderFor } from "../test-utils/renderFor"; import { renderFor } from "../test-utils/renderFor";
import type { Cluster } from "../../../common/cluster/cluster"; import type { Cluster } from "../../../common/cluster/cluster";
import type { ApiManager } from "../../../common/k8s-api/api-manager"; import type { ApiManager } from "../../../common/k8s-api/api-manager";
import apiManagerInjectable from "./dependencies/api-manager.injectable"; import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
import { KubeObjectMenu } from "./index"; import { KubeObjectMenu } from "./index";
import type { KubeObjectMenuRegistration } from "./dependencies/kube-object-menu-items/kube-object-menu-registration"; import type { KubeObjectMenuRegistration } from "./dependencies/kube-object-menu-items/kube-object-menu-registration";
import { computed } from "mobx"; import { computed } from "mobx";

View File

@ -15,7 +15,7 @@ import clusterNameInjectable from "./dependencies/cluster-name.injectable";
import createEditResourceTabInjectable from "../dock/edit-resource/edit-resource-tab.injectable"; import createEditResourceTabInjectable from "../dock/edit-resource/edit-resource-tab.injectable";
import hideDetailsInjectable from "./dependencies/hide-details.injectable"; import hideDetailsInjectable from "./dependencies/hide-details.injectable";
import kubeObjectMenuItemsInjectable from "./dependencies/kube-object-menu-items/kube-object-menu-items.injectable"; import kubeObjectMenuItemsInjectable from "./dependencies/kube-object-menu-items/kube-object-menu-items.injectable";
import apiManagerInjectable from "./dependencies/api-manager.injectable"; import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
export interface KubeObjectMenuProps<TKubeObject extends KubeObject> extends MenuActionsProps { export interface KubeObjectMenuProps<TKubeObject extends KubeObject> extends MenuActionsProps {
object: TKubeObject; object: TKubeObject;