diff --git a/packages/backend/src/plugins/kubernetes.ts b/packages/backend/src/plugins/kubernetes.ts index 60991d3..a831f11 100644 --- a/packages/backend/src/plugins/kubernetes.ts +++ b/packages/backend/src/plugins/kubernetes.ts @@ -1,4 +1,4 @@ -import { KubernetesBuilder } from '@backstage/plugin-kubernetes-backend'; +import {KubernetesBuilder, OidcKubernetesAuthTranslator} from '@backstage/plugin-kubernetes-backend'; import { Router } from 'express'; import { PluginEnvironment } from '../types'; import { CatalogClient } from '@backstage/catalog-client'; @@ -11,6 +11,9 @@ export default async function createPlugin( logger: env.logger, config: env.config, catalogApi, + permissions: env.permissions }).build(); + + new OidcKubernetesAuthTranslator() return router; } diff --git a/packages/backend/src/plugins/workflow-argo.ts b/packages/backend/src/plugins/workflow-argo.ts index 3053c2b..e4125d9 100644 --- a/packages/backend/src/plugins/workflow-argo.ts +++ b/packages/backend/src/plugins/workflow-argo.ts @@ -3,12 +3,11 @@ import { Config } from '@backstage/config'; import * as k8s from '@kubernetes/client-node'; import {Logger} from "winston"; import {HttpError} from "@kubernetes/client-node"; -import {useApi} from "@backstage/core-plugin-api"; -import {OidcKubernetesAuthTranslator} from "@backstage/plugin-kubernetes-backend"; type argoInput = { namespace: string clusterName: string + userOIDCToken: string templateName: string parameters: parameter[] wait?: boolean @@ -100,9 +99,14 @@ export function createInvokeArgoAction(config: Config, logger: Logger) { description: 'Name of Cluster', type: 'string', }, + userOIDCToken: { + title: 'User\'s OIDC token', + description: "If specified, it will use the provided token to communicate with the Kubernetes cluster", + type: 'string' + }, templateName: { title: 'Template name', - description: 'Argo Workflows template name', + description: 'Argo Workflows template name to run', type: 'string', }, parameters: { @@ -123,7 +127,7 @@ export function createInvokeArgoAction(config: Config, logger: Logger) { }, wait: { title: 'Wait for completion', - description: 'specify weather to wait for completion of this workflow', + description: 'specify weather to wait for completion of this workflow.', type: 'boolean', } }, @@ -145,78 +149,78 @@ export function createInvokeArgoAction(config: Config, logger: Logger) { async handler(ctx: ActionContext) { logger.debug(`Invoked with ${JSON.stringify(ctx.input)})`) logger.info(JSON.stringify(ctx.secrets)) - // const targetCluster = getClusterConfig(ctx.input.clusterName, config) - // const kc = new k8s.KubeConfig() - // kc.addCluster({ - // name: ctx.input.clusterName, - // caData: targetCluster.getString("caData"), - // server: targetCluster.getString("url"), - // skipTLSVerify: targetCluster.getBoolean("skipTLSVerify"), - // }) - // kc.addUser({ - // name: "admin", - // token: targetCluster.getString("serviceAccountToken") - // }) - // kc.addContext({ - // cluster: ctx.input.clusterName, - // user: "admin", - // name: ctx.input.clusterName - // }) - // kc.setCurrentContext(ctx.input.clusterName) - // - // const client = kc.makeApiClient(k8s.CustomObjectsApi) - // const wf = new Workflow(ctx.input.templateName, ctx.input.namespace, ctx.input.parameters) - // // const body = generateBody(ctx.input.templateName, ctx.input.namespace) - // try { - // const resp = await client.createNamespacedCustomObject( - // argoWorkflowsGroup, argoWorkflowsVersion, ctx.input.namespace, - // argoWorkFlowPlural, wf - // ) - // const respBody = resp.body as Workflow - // logger.debug(`Workflow ID: ${respBody.metadata.name}, namespace ${respBody.metadata.namespace}`) - // ctx.output('workflowName', respBody.metadata.name!) - // ctx.output('workflowNamespace', respBody.metadata.namespace!) - // if (ctx.input.wait) { - // await wait(kc, respBody.metadata.namespace!, respBody.metadata.name!) - // } - // } catch (err) { - // if (err instanceof HttpError) { - // let msg = `${err.response.statusMessage}: ` - // if ("kind" in err.body && err.body.kind === "Status" && "message" in err.body) { - // msg += err.body.message - // } - // logger.info(`error : ${err.response.statusCode} ${msg}`) - // throw new Error(`Failed to talk to the cluster: ${err.response.statusCode} ${err.response.statusMessage} \n ${msg}`) - // } - // if (err instanceof Error) { - // logger.error(`error while talking to cluster: ${err.name} ${err.message}`) - // } - // throw new Error("Unknown exception was encountered.") - // } + const targetCluster = getClusterConfig(ctx.input.clusterName, config) + const kc = new k8s.KubeConfig() + kc.addCluster({ + name: targetCluster.getString("name"), + caData: targetCluster.getString("caData"), + server: targetCluster.getString("url"), + skipTLSVerify: targetCluster.getBoolean("skipTLSVerify"), + }) + + kc.addUser({ + name: "scaffolder-user", + token: ctx.input.userOIDCToken? ctx.input.userOIDCToken : targetCluster.getString("serviceAccountToken") + }) + kc.addContext({ + cluster: ctx.input.clusterName, + user: "scaffolder-user", + name: ctx.input.clusterName + }) + kc.setCurrentContext(ctx.input.clusterName) + + const client = kc.makeApiClient(k8s.CustomObjectsApi) + const wf = new Workflow(ctx.input.templateName, ctx.input.namespace, ctx.input.parameters) + // const body = generateBody(ctx.input.templateName, ctx.input.namespace) + try { + const resp = await client.createNamespacedCustomObject( + argoWorkflowsGroup, argoWorkflowsVersion, ctx.input.namespace, + argoWorkFlowPlural, wf + ) + const respBody = resp.body as Workflow + logger.debug(`Workflow ID: ${respBody.metadata.name}, namespace ${respBody.metadata.namespace}`) + ctx.output('workflowName', respBody.metadata.name!) + ctx.output('workflowNamespace', respBody.metadata.namespace!) + if (ctx.input.wait) { + await wait(kc, respBody.metadata.namespace!, respBody.metadata.name!) + } + } catch (err) { + if (err instanceof HttpError) { + let msg = `${err.response.statusMessage}: ` + if ("kind" in err.body && err.body.kind === "Status" && "message" in err.body) { + msg += err.body.message + } + logger.info(`error : ${err.response.statusCode} ${msg}`) + throw new Error(`Failed to talk to the cluster: ${err.response.statusCode} ${err.response.statusMessage} \n ${msg}`) + } + if (err instanceof Error) { + logger.error(`error while talking to cluster: ${err.name} ${err.message}`) + } + throw new Error("Unknown exception was encountered.") + } } } ) } function getClusterConfig(name: string, config: Config): Config { - const at = new OidcKubernetesAuthTranslator(); - - const c = config.getConfigArray("kubernetes.clusterLocatorMethods") - const cc = c.filter(function(val) { - return val.getString("type") === "config" - }) + const clusterConfigs = config.getConfigArray("kubernetes.clusterLocatorMethods").filter( + (val: Config) => { + return val.getString('type') === 'config' + } + ) const clusters = new Array(); - // this is shit - cc.forEach(function(conf ) { - const cl = conf.getConfigArray("clusters") - cl.forEach(function(val) { - if (val.getString("name") === name) { - clusters.push(val) - } + clusterConfigs.filter( (conf: Config) => { + const cluster = conf.getConfigArray("clusters").find( (val: Config) => { + return val.getString("name") === name }) + if (cluster) { + clusters.push(cluster) + } }) + if (clusters.length === 0 ) { throw new Error(`Cluster with name ${name} not found`) }