diff --git a/src/common/k8s-api/__tests__/kube-api.test.ts b/src/common/k8s-api/__tests__/kube-api.test.ts index 33c2f9dc06..2ae2015437 100644 --- a/src/common/k8s-api/__tests__/kube-api.test.ts +++ b/src/common/k8s-api/__tests__/kube-api.test.ts @@ -33,7 +33,7 @@ class TestKubeObject extends KubeObject { static apiBase = "/api/v1/pods"; } -class TestKubeApi extends KubeApi {} +class TestKubeApi extends KubeApi { } describe("forRemoteCluster", () => { it("builds api client for KubeObject", async () => { @@ -474,4 +474,142 @@ describe("KubeApi", () => { }); }); }); + + describe("create", () => { + let api: TestKubeApi; + + beforeEach(() => { + api = new TestKubeApi({ + request, + objectConstructor: TestKubeObject, + }); + }); + + it("should add kind and apiVersion", async () => { + expect.hasAssertions(); + + (fetch as any).mockResponse(async (request: Request) => { + expect(request.method).toEqual("POST"); + expect(JSON.parse(request.body.toString())).toEqual({ + kind: "Pod", + apiVersion: "v1", + metadata: { + name: "foobar", + namespace: "default", + }, + spec: { + containers: [ + { + name: "web", + image: "nginx", + ports: [ + { + name: "web", + containerPort: 80, + protocol: "TCP", + }, + ], + }, + ], + }, + }); + + return {}; + }); + + await api.create({ + name: "foobar", + namespace: "default", + }, { + spec: { + containers: [ + { + name: "web", + image: "nginx", + ports: [ + { + name: "web", + containerPort: 80, + protocol: "TCP", + }, + ], + }, + ], + }, + }); + }); + + it("doesn't override metadata.labels", async () => { + expect.hasAssertions(); + + (fetch as any).mockResponse(async (request: Request) => { + expect(request.method).toEqual("POST"); + expect(JSON.parse(request.body.toString())).toEqual({ + kind: "Pod", + apiVersion: "v1", + metadata: { + name: "foobar", + namespace: "default", + labels: { + foo: "bar", + }, + }, + }); + + return {}; + }); + + await api.create({ + name: "foobar", + namespace: "default", + }, { + metadata: { + labels: { + foo: "bar", + }, + }, + }); + }); + }); + + describe("update", () => { + let api: TestKubeApi; + + beforeEach(() => { + api = new TestKubeApi({ + request, + objectConstructor: TestKubeObject, + }); + }); + + it("doesn't override metadata.labels", async () => { + expect.hasAssertions(); + + (fetch as any).mockResponse(async (request: Request) => { + expect(request.method).toEqual("PUT"); + expect(JSON.parse(request.body.toString())).toEqual({ + metadata: { + name: "foobar", + namespace: "default", + labels: { + foo: "bar", + }, + }, + }); + + return {}; + }); + + await api.update({ + name: "foobar", + namespace: "default", + }, { + metadata: { + labels: { + foo: "bar", + }, + }, + }); + }); + }); }); diff --git a/src/common/k8s-api/kube-api.ts b/src/common/k8s-api/kube-api.ts index 923c41c360..33cecf3db1 100644 --- a/src/common/k8s-api/kube-api.ts +++ b/src/common/k8s-api/kube-api.ts @@ -21,7 +21,7 @@ // Base class for building all kubernetes apis -import { isFunction } from "lodash"; +import { isFunction, merge } from "lodash"; import { stringify } from "querystring"; import { apiKubePrefix, isDevelopment } from "../../common/vars"; import logger from "../../main/logger"; @@ -102,7 +102,11 @@ export type PropagationPolicy = undefined | "Orphan" | "Foreground" | "Backgroun /** * @deprecated */ -export interface IKubeApiCluster extends ILocalKubeApiConfig {} +export interface IKubeApiCluster extends ILocalKubeApiConfig { } + +export type PartialKubeObject = Partial> & { + metadata?: Partial, +}; export interface IRemoteKubeApiConfig { cluster: { @@ -478,18 +482,19 @@ export class KubeApi { return parsed; } - async create({ name, namespace }: Partial, data?: Partial): Promise { + async create({ name, namespace }: Partial, data?: PartialKubeObject): Promise { await this.checkPreferredVersion(); const apiUrl = this.getUrl({ namespace }); const res = await this.request.post(apiUrl, { - data: { - ...data, + data: merge(data, { + kind: this.kind, + apiVersion: this.apiVersionWithGroup, metadata: { name, namespace, }, - }, + }), }); const parsed = this.parseResponse(res); @@ -500,18 +505,17 @@ export class KubeApi { return parsed; } - async update({ name, namespace }: ResourceDescriptor, data: Partial): Promise { + async update({ name, namespace }: ResourceDescriptor, data: PartialKubeObject): Promise { await this.checkPreferredVersion(); const apiUrl = this.getUrl({ namespace, name }); const res = await this.request.put(apiUrl, { - data: { - ...data, + data: merge(data, { metadata: { name, namespace, }, - }, + }), }); const parsed = this.parseResponse(res); @@ -580,7 +584,7 @@ export class KubeApi { clearTimeout(timedRetry); }); - const requestParams = timeout ? { query: { timeoutSeconds: timeout }}: {}; + const requestParams = timeout ? { query: { timeoutSeconds: timeout }} : {}; const watchUrl = this.getWatchUrl(namespace); const responsePromise = this.request.getResponse(watchUrl, requestParams, { signal: abortController.signal,