From 91d2ccbb952ab982d502e2ee6f5659a9cde5c1ed Mon Sep 17 00:00:00 2001 From: Manabu Mccloskey Date: Tue, 25 Jul 2023 18:00:42 -0700 Subject: [PATCH] refactor k8s cluster picker --- packages/app/src/App.tsx | 4 +- .../src/scaffolder/credentials/extensions.ts | 14 --- .../scaffolder/credentials/getOIDCToken.tsx | 102 ------------------ .../app/src/scaffolder/credentials/index.ts | 1 - .../app/src/scaffolder/credentials/schema.ts | 20 ---- .../KubernetesClusterPicker.tsx | 87 +++++++++++++++ .../kubernetesClusterPicker/extensions.ts | 12 +++ .../kubernetesClusterPicker/index.ts | 1 + .../kubernetesClusterPicker/schema.ts | 31 ++++++ 9 files changed, 133 insertions(+), 139 deletions(-) delete mode 100644 packages/app/src/scaffolder/credentials/extensions.ts delete mode 100644 packages/app/src/scaffolder/credentials/getOIDCToken.tsx delete mode 100644 packages/app/src/scaffolder/credentials/index.ts delete mode 100644 packages/app/src/scaffolder/credentials/schema.ts create mode 100644 packages/app/src/scaffolder/kubernetesClusterPicker/KubernetesClusterPicker.tsx create mode 100644 packages/app/src/scaffolder/kubernetesClusterPicker/extensions.ts create mode 100644 packages/app/src/scaffolder/kubernetesClusterPicker/index.ts create mode 100644 packages/app/src/scaffolder/kubernetesClusterPicker/schema.ts diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 4d74dd8..92f625d 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -38,7 +38,7 @@ import { AppRouter, FlatRoutes } from '@backstage/core-app-api'; import { CatalogGraphPage } from '@backstage/plugin-catalog-graph'; import { RequirePermission } from '@backstage/plugin-permission-react'; import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common/alpha'; -import { GetK8sOIDCTokenExtension } from './scaffolder/credentials'; +import { KubernetesClusterPickerExtension } from './scaffolder/kubernetesClusterPicker'; const app = createApp({ apis, @@ -95,7 +95,7 @@ const routes = ( }> - + } /> diff --git a/packages/app/src/scaffolder/credentials/extensions.ts b/packages/app/src/scaffolder/credentials/extensions.ts deleted file mode 100644 index 8313c7b..0000000 --- a/packages/app/src/scaffolder/credentials/extensions.ts +++ /dev/null @@ -1,14 +0,0 @@ -import {scaffolderPlugin,} from '@backstage/plugin-scaffolder'; -import {createScaffolderFieldExtension} from "@backstage/plugin-scaffolder-react"; -import {GetK8sOIDCToken} from "./getOIDCToken"; -import {ClusterPickerSchema} from "./schema"; - -export const GetK8sOIDCTokenExtension = scaffolderPlugin.provide( - createScaffolderFieldExtension( - { - name: 'GetK8sOIDCToken', - component: GetK8sOIDCToken, - schema: ClusterPickerSchema, - } - ) -) diff --git a/packages/app/src/scaffolder/credentials/getOIDCToken.tsx b/packages/app/src/scaffolder/credentials/getOIDCToken.tsx deleted file mode 100644 index 9614852..0000000 --- a/packages/app/src/scaffolder/credentials/getOIDCToken.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import React, {useCallback, useEffect} from 'react'; -import FormControl from '@material-ui/core/FormControl'; -import { - useApi, configApiRef, discoveryApiRef, oauthRequestApiRef -} from "@backstage/core-plugin-api"; -import { kubernetesApiRef } from "@backstage/plugin-kubernetes"; -import { FormHelperText } from "@material-ui/core"; -import {Progress, Select} from "@backstage/core-components"; -import useAsync from "react-use/lib/useAsync"; -import {useTemplateSecrets} from "@backstage/plugin-scaffolder-react"; -import {ClusterPickerProps} from "./schema"; -import {OAuth2} from "@backstage/core-app-api"; - - -export const GetK8sOIDCToken = (props: ClusterPickerProps) => { - - const k8sApi = useApi(kubernetesApiRef) - const { setSecrets, secrets } = useTemplateSecrets(); - - const discoveryApi = useApi(discoveryApiRef) - const oauthRequestApi = useApi(oauthRequestApiRef) - const configApi = useApi(configApiRef) - - const {uiSchema, required} = props - let {rawErrors} = props - const {value: {clusters} = {clusters: []}, loading } = useAsync( - async () => { - const c = await k8sApi.getClusters() - return {clusters: c.map(i => ({ label: i.name, value: i.name}))} - } - ) - if (!rawErrors) { - rawErrors = [] - } - - const getToken = useCallback( async (clusterName: string) => { - const {requestUserCredentials} = uiSchema?.['ui:options'] ?? {} - if (!requestUserCredentials) { - return; - } - const cs = await k8sApi.getClusters() - const cluster = cs.find(c => { - return c.name === clusterName - }) - if (cluster?.oidcTokenProvider === undefined) { - throw new Error("no oidc provider defined for this cluster") - } - - const oidc = OAuth2.create({ - discoveryApi, - oauthRequestApi, - provider: { - id: cluster.oidcTokenProvider, - title: 'OIDC', - icon: () => null, - }, - environment: configApi.getOptionalString('auth.environment'), - defaultScopes: ['openid', 'profile', 'email', 'groups'], - }) - const token = await oidc.getIdToken() - - setSecrets({ [requestUserCredentials.secretKey]: token }) - }, [configApi, discoveryApi, k8sApi, oauthRequestApi, setSecrets, uiSchema] - ) - - useEffect(() => { - const {requestUserCredentials} = uiSchema?.['ui:options'] ?? {} - if (!requestUserCredentials?.secretKey || secrets[requestUserCredentials?.secretKey!]) { - return - } - - if (clusters.length) { - getToken(clusters[0].value).catch(console.error) - } - }, [clusters, getToken, secrets, uiSchema]) - - if (loading) { - return ; - } - - return ( - 0} - > - { + onChange(e.toString()); + getToken(e.toString()); + }} + /> + Kubernetes Cluster Name + + ); +}; diff --git a/packages/app/src/scaffolder/kubernetesClusterPicker/extensions.ts b/packages/app/src/scaffolder/kubernetesClusterPicker/extensions.ts new file mode 100644 index 0000000..bad90c5 --- /dev/null +++ b/packages/app/src/scaffolder/kubernetesClusterPicker/extensions.ts @@ -0,0 +1,12 @@ +import { scaffolderPlugin } from '@backstage/plugin-scaffolder'; +import { createScaffolderFieldExtension } from '@backstage/plugin-scaffolder-react'; +import { KubernetesClusterPicker } from './KubernetesClusterPicker'; +import { ClusterPickerSchema } from './schema'; + +export const KubernetesClusterPickerExtension = scaffolderPlugin.provide( + createScaffolderFieldExtension({ + name: 'KubernetesClusterPicker', + component: KubernetesClusterPicker, + schema: ClusterPickerSchema, + }), +); diff --git a/packages/app/src/scaffolder/kubernetesClusterPicker/index.ts b/packages/app/src/scaffolder/kubernetesClusterPicker/index.ts new file mode 100644 index 0000000..dbf85db --- /dev/null +++ b/packages/app/src/scaffolder/kubernetesClusterPicker/index.ts @@ -0,0 +1 @@ +export { KubernetesClusterPickerExtension } from './extensions'; diff --git a/packages/app/src/scaffolder/kubernetesClusterPicker/schema.ts b/packages/app/src/scaffolder/kubernetesClusterPicker/schema.ts new file mode 100644 index 0000000..e5dac30 --- /dev/null +++ b/packages/app/src/scaffolder/kubernetesClusterPicker/schema.ts @@ -0,0 +1,31 @@ +import { z } from 'zod'; +import { makeFieldSchemaFromZod } from '@backstage/plugin-scaffolder'; + +export const ClusterPickerFieldSchema = makeFieldSchemaFromZod( + z.string(), + z.object({ + requestUserCredentials: z + .object({ + secretKey: z + .string() + .describe( + 'Key used within the template secrets context to store the credential', + ), + }) + .optional() + .describe( + 'If defined will request user credentials to auth against the cluster', + ), + allowedClusters: z + .array(z.string()) + .optional() + .describe('List of allowed Kubernetes clusters'), + }), +); + +export const ClusterPickerSchema = ClusterPickerFieldSchema.schema; + +export type ClusterPickerProps = typeof ClusterPickerFieldSchema.type; + +export type ClusterPickerUiOptions = + typeof ClusterPickerFieldSchema.uiOptionsType;