From 3262b13926fa575984b4e9accef09ad26c2024d9 Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Mon, 13 Sep 2021 10:14:05 +0300 Subject: [PATCH] Allow KubeApi connect directly to remote k8s (#3766) * allow KubeApi to connect directly to remote k8s Signed-off-by: Jari Kolehmainen * backward compat Signed-off-by: Jari Kolehmainen * backward compat Signed-off-by: Jari Kolehmainen --- src/common/k8s-api/__tests__/kube-api.test.ts | 28 +++++++- src/common/k8s-api/kube-api.ts | 66 ++++++++++++++++++- src/extensions/main-api/k8s-api.ts | 2 +- src/extensions/renderer-api/k8s-api.ts | 2 +- 4 files changed, 93 insertions(+), 5 deletions(-) diff --git a/src/common/k8s-api/__tests__/kube-api.test.ts b/src/common/k8s-api/__tests__/kube-api.test.ts index 1e1bac05fe..363d3176f5 100644 --- a/src/common/k8s-api/__tests__/kube-api.test.ts +++ b/src/common/k8s-api/__tests__/kube-api.test.ts @@ -19,10 +19,36 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { KubeApi } from "../kube-api"; +import { Pod } from "../endpoints/pods.api"; +import { forRemoteCluster, KubeApi } from "../kube-api"; import { KubeJsonApi } from "../kube-json-api"; import { KubeObject } from "../kube-object"; +describe("forRemoteCluster", () => { + it("builds api client", async (done) => { + const api = forRemoteCluster({ + cluster: { + server: "https://127.0.0.1:6443" + }, + user: { + token: "daa" + } + }, Pod); + + (fetch as any).mockResponse(async (request: any) => { + expect(request.url).toEqual("https://127.0.0.1:6443/api/v1/pods"); + + done(); + + return { + body: "" + }; + }); + + await api.list(); + }); +}); + describe("KubeApi", () => { let request: KubeJsonApi; diff --git a/src/common/k8s-api/kube-api.ts b/src/common/k8s-api/kube-api.ts index 33ed422601..54b0e5726f 100644 --- a/src/common/k8s-api/kube-api.ts +++ b/src/common/k8s-api/kube-api.ts @@ -35,6 +35,7 @@ import { KubeJsonApi, KubeJsonApiData } from "./kube-json-api"; import { noop } from "../utils"; import type { RequestInit } from "node-fetch"; import AbortController from "abort-controller"; +import { Agent, AgentOptions } from "https"; export interface IKubeApiOptions { /** @@ -89,13 +90,31 @@ export interface IKubeResourceList { }[]; } -export interface IKubeApiCluster { +export interface ILocalKubeApiConfig { metadata: { uid: string; } } -export function forCluster(cluster: IKubeApiCluster, kubeClass: KubeObjectConstructor): KubeApi { +/** + * @deprecated + */ +export interface IKubeApiCluster extends ILocalKubeApiConfig {} + +export interface IRemoteKubeApiConfig { + cluster: { + server: string; + caData?: string; + skipTLSVerify?: boolean; + } + user: { + token?: string; + clientCertificateData?: string; + clientKeyData?: string; + } +} + +export function forCluster(cluster: ILocalKubeApiConfig, kubeClass: KubeObjectConstructor): KubeApi { const url = new URL(apiBase.config.serverAddress); const request = new KubeJsonApi({ serverAddress: apiBase.config.serverAddress, @@ -113,6 +132,49 @@ export function forCluster(cluster: IKubeApiCluster, kubeC }); } +export function forRemoteCluster(config: IRemoteKubeApiConfig, kubeClass: KubeObjectConstructor): KubeApi { + const reqInit: RequestInit = {}; + + if (config.user.token) { + reqInit.headers = { + "Authorization": `Bearer ${config.user.token}` + }; + } + + const agentOptions: AgentOptions = {}; + + if (config.cluster.skipTLSVerify === true) { + agentOptions.rejectUnauthorized = false; + } + + if (config.user.clientCertificateData) { + agentOptions.cert = config.user.clientCertificateData; + } + + if (config.user.clientKeyData) { + agentOptions.key = config.user.clientKeyData; + } + + if (config.cluster.caData) { + agentOptions.ca = config.cluster.caData; + } + + if (Object.keys(agentOptions).length > 0) { + reqInit.agent = new Agent(agentOptions); + } + + const request = new KubeJsonApi({ + serverAddress: config.cluster.server, + apiBase: "", + debug: isDevelopment, + }, reqInit); + + return new KubeApi({ + objectConstructor: kubeClass, + request + }); +} + export function ensureObjectSelfLink(api: KubeApi, object: KubeJsonApiData) { if (!object.metadata.selfLink) { object.metadata.selfLink = createKubeApiURL({ diff --git a/src/extensions/main-api/k8s-api.ts b/src/extensions/main-api/k8s-api.ts index 0664cbc1d9..9a73e654e4 100644 --- a/src/extensions/main-api/k8s-api.ts +++ b/src/extensions/main-api/k8s-api.ts @@ -56,6 +56,6 @@ export { ClusterRoleBinding, clusterRoleBindingApi } from "../../common/k8s-api/ export { CustomResourceDefinition, crdApi } from "../../common/k8s-api/endpoints/crd.api"; // types -export type { IKubeApiCluster } from "../../common/k8s-api/kube-api"; +export type { ILocalKubeApiConfig, IRemoteKubeApiConfig, IKubeApiCluster } from "../../common/k8s-api/kube-api"; export type { IPodContainer, IPodContainerStatus } from "../../common/k8s-api/endpoints/pods.api"; export type { ISecretRef } from "../../common/k8s-api/endpoints/secret.api"; diff --git a/src/extensions/renderer-api/k8s-api.ts b/src/extensions/renderer-api/k8s-api.ts index 1622dd6f3d..d6b9663f5e 100644 --- a/src/extensions/renderer-api/k8s-api.ts +++ b/src/extensions/renderer-api/k8s-api.ts @@ -57,7 +57,7 @@ export { CustomResourceDefinition, crdApi } from "../../common/k8s-api/endpoints export { KubeObjectStatusLevel } from "./kube-object-status"; // types -export type { IKubeApiCluster } from "../../common/k8s-api/kube-api"; +export type { ILocalKubeApiConfig, IRemoteKubeApiConfig, IKubeApiCluster } from "../../common/k8s-api/kube-api"; export type { IPodContainer, IPodContainerStatus } from "../../common/k8s-api/endpoints"; export type { ISecretRef } from "../../common/k8s-api/endpoints"; export type { KubeObjectStatus } from "./kube-object-status";