diff --git a/src/common/k8s-api/__tests__/kube-api.test.ts b/src/common/k8s-api/__tests__/kube-api.test.ts index 7fffcbd1f6..b50403174e 100644 --- a/src/common/k8s-api/__tests__/kube-api.test.ts +++ b/src/common/k8s-api/__tests__/kube-api.test.ts @@ -133,7 +133,10 @@ describe("KubeApi", () => { checkPreferredVersion: true, }); - await kubeApi.get(); + await kubeApi.get({ + name: "foo", + namespace: "default", + }); expect(kubeApi.apiPrefix).toEqual("/apis"); expect(kubeApi.apiGroup).toEqual("networking.k8s.io"); }); @@ -173,7 +176,10 @@ describe("KubeApi", () => { checkPreferredVersion: true, }); - await kubeApi.get(); + await kubeApi.get({ + name: "foo", + namespace: "default", + }); expect(kubeApi.apiPrefix).toEqual("/apis"); expect(kubeApi.apiGroup).toEqual("extensions"); }); @@ -312,7 +318,7 @@ describe("KubeApi", () => { (fetch as any).mockResponse(async () => { return {}; }); - + api.watch({ namespace: "kube-system" }); expect(spy).toHaveBeenCalledWith("/api/v1/namespaces/kube-system/pods?watch=1&resourceVersion=", expect.anything(), expect.anything()); }); @@ -323,7 +329,7 @@ describe("KubeApi", () => { (fetch as any).mockResponse(async () => { return {}; }); - + api.watch({ namespace: "kube-system", timeout: 60 }); expect(spy).toHaveBeenCalledWith("/api/v1/namespaces/kube-system/pods?watch=1&resourceVersion=", { query: { timeoutSeconds: 60 }}, expect.anything()); }); diff --git a/src/common/k8s-api/kube-api.ts b/src/common/k8s-api/kube-api.ts index 0684bc184b..8e90efd74f 100644 --- a/src/common/k8s-api/kube-api.ts +++ b/src/common/k8s-api/kube-api.ts @@ -21,7 +21,6 @@ // Base class for building all kubernetes apis -import merge from "lodash/merge"; import { isFunction } from "lodash"; import { stringify } from "querystring"; import { apiKubePrefix, isDevelopment } from "../../common/vars"; @@ -221,6 +220,29 @@ const patchTypeHeaders: Record = { "strategic": "application/strategic-merge-patch+json", }; +export interface ResourceDescriptor { + /** + * The name of the kubernetes resource + */ + name: string; + + /** + * The namespace that the resource lives in (if the resource is namespaced) + * + * Note: if not provided and the resource kind is namespaced, then this defaults to `"default"` + */ + namespace?: string; +} + +export interface DeleteResourceDescriptor extends ResourceDescriptor { + /** + * This determinines how child resources should be handled by kubernetes + * + * @default "Background" + */ + propagationPolicy?: PropagationPolicy; +} + export class KubeApi { readonly kind: string; readonly apiBase: string; @@ -355,12 +377,12 @@ export class KubeApi { return this.list(params, { limit: 1 }); } - getUrl({ name = "", namespace = "" } = {}, query?: Partial) { + getUrl({ name, namespace = "default" }: Partial = {}, query?: Partial) { const resourcePath = createKubeApiURL({ apiPrefix: this.apiPrefix, apiVersion: this.apiVersionWithGroup, resource: this.apiResource, - namespace: this.isNamespaced ? namespace : undefined, + namespace: this.isNamespaced ? namespace ?? "default" : undefined, name, }); @@ -438,10 +460,10 @@ export class KubeApi { throw new Error(`GET multiple request to ${url} returned not an array: ${JSON.stringify(parsed)}`); } - async get({ name = "", namespace = "default" } = {}, query?: IKubeApiQueryParams): Promise { + async get(desc: ResourceDescriptor, query?: IKubeApiQueryParams): Promise { await this.checkPreferredVersion(); - const url = this.getUrl({ namespace, name }); + const url = this.getUrl(desc); const res = await this.request.get(url, { query }); const parsed = this.parseResponse(res); @@ -452,19 +474,18 @@ export class KubeApi { return parsed; } - async create({ name = "", namespace = "default" } = {}, data?: Partial): Promise { + async create({ name, namespace }: Partial, data?: Partial): Promise { await this.checkPreferredVersion(); const apiUrl = this.getUrl({ namespace }); const res = await this.request.post(apiUrl, { - data: merge({ - kind: this.kind, - apiVersion: this.apiVersionWithGroup, + data: { + ...data, metadata: { name, namespace, }, - }, data), + }, }); const parsed = this.parseResponse(res); @@ -475,11 +496,19 @@ export class KubeApi { return parsed; } - async update({ name = "", namespace = "default" } = {}, data?: Partial): Promise { + async update({ name, namespace }: ResourceDescriptor, data: Partial): Promise { await this.checkPreferredVersion(); const apiUrl = this.getUrl({ namespace, name }); - const res = await this.request.put(apiUrl, { data }); + const res = await this.request.put(apiUrl, { + data: { + ...data, + metadata: { + name, + namespace, + }, + }, + }); const parsed = this.parseResponse(res); if (Array.isArray(parsed)) { @@ -489,9 +518,9 @@ export class KubeApi { return parsed; } - async patch({ name = "", namespace = "default" } = {}, data?: Partial | Patch, strategy: KubeApiPatchType = "strategic"): Promise { + async patch(desc: ResourceDescriptor, data?: Partial | Patch, strategy: KubeApiPatchType = "strategic"): Promise { await this.checkPreferredVersion(); - const apiUrl = this.getUrl({ namespace, name }); + const apiUrl = this.getUrl(desc); const res = await this.request.patch(apiUrl, { data }, { headers: { @@ -507,16 +536,15 @@ export class KubeApi { return parsed; } - async delete({ name = "", namespace = "default", propagationPolicy = "Background" }: { name: string, namespace?: string, propagationPolicy?: PropagationPolicy }) { + async delete({ propagationPolicy = "Background", ...desc }: DeleteResourceDescriptor) { await this.checkPreferredVersion(); - const apiUrl = this.getUrl({ namespace, name }); - const reqInit = { + const apiUrl = this.getUrl(desc); + + return this.request.del(apiUrl, { query: { propagationPolicy, }, - }; - - return this.request.del(apiUrl, reqInit); + }); } getWatchUrl(namespace = "", query: IKubeApiQueryParams = {}) { diff --git a/src/common/k8s-api/kube-object.store.ts b/src/common/k8s-api/kube-object.store.ts index 035f21d2d5..5a00c5a7b4 100644 --- a/src/common/k8s-api/kube-object.store.ts +++ b/src/common/k8s-api/kube-object.store.ts @@ -363,7 +363,8 @@ export abstract class KubeObjectStore extends ItemStore return this.postUpdate( await this.api.update( { - name: item.getName(), namespace: item.getNs(), + name: item.getName(), + namespace: item.getNs(), }, data, ),