This commit is contained in:
Manabu Mccloskey 2023-02-23 15:51:07 -08:00
parent 7e3bc0ac78
commit 3fa6a89fea
8 changed files with 175 additions and 7 deletions

View file

@ -37,9 +37,12 @@ backend:
integrations:
github:
- host: github.com
# This is a Personal Access Token or PAT from GitHub. You can find out how to generate this token, and more information
# about setting up the GitHub integration here: https://backstage.io/docs/getting-started/configuration#setting-up-a-github-integration
token: ${GITHUB_TOKEN}
apps:
- $include: github-integration.yaml
# - host: github.com
# # This is a Personal Access Token or PAT from GitHub. You can find out how to generate this token, and more information
# # about setting up the GitHub integration here: https://backstage.io/docs/getting-started/configuration#setting-up-a-github-integration
# token: ${GITHUB_TOKEN}
### Example for how to add your GitHub Enterprise instance using the API:
# - host: ghe.example.net
# apiBaseUrl: https://ghe.example.net/api/v3
@ -75,12 +78,15 @@ catalog:
entityFilename: catalog-info.yaml
pullRequestBranchName: backstage-integration
rules:
- allow: [Component, System, API, Resource, Location]
- allow: [Component, System, API, Resource, Location, Template]
locations:
# Local example data, file locations are relative to the backend process, typically `packages/backend`
- type: file
target: ../../examples/entities.yaml
- type: file
target: /Users/mccloman/repos/backstage-templates/template1.yaml
- type: file
target: /Users/mccloman/repos/backstage-app/backstage/test-template.yaml
# Local example template
- type: file
target: ../../examples/template/template.yaml
@ -102,3 +108,8 @@ catalog:
# target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/acme-corp.yaml
# rules:
# - allow: [User, Group]
kubernetes:
serviceLocatorMethod:
type: 'multiTenant'
clusterLocatorMethods:
- $include: k8s-config.yaml

View file

@ -32,6 +32,7 @@
"@backstage/plugin-catalog-import": "^0.9.5",
"@backstage/plugin-catalog-react": "^1.3.0",
"@backstage/plugin-github-actions": "^0.5.15",
"@backstage/plugin-kubernetes": "^0.7.8",
"@backstage/plugin-org": "^0.6.5",
"@backstage/plugin-permission-react": "^0.4.10",
"@backstage/plugin-scaffolder": "^1.11.0",

View file

@ -56,6 +56,8 @@ import {
import { TechDocsAddons } from '@backstage/plugin-techdocs-react';
import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib';
import { EntityKubernetesContent } from '@backstage/plugin-kubernetes';
const techdocsContent = (
<EntityTechdocsContent>
<TechDocsAddons>
@ -342,6 +344,9 @@ const systemPage = (
unidirectional={false}
/>
</EntityLayout.Route>
<EntityLayout.Route path="/kubernetes" title="Kubernetes">
<EntityKubernetesContent refreshIntervalMs={30000} />
</EntityLayout.Route>
</EntityLayout>
);

View file

@ -21,18 +21,22 @@
"@backstage/catalog-client": "^1.3.1",
"@backstage/catalog-model": "^1.2.0",
"@backstage/config": "^1.0.6",
"@backstage/integration": "^1.4.2",
"@backstage/plugin-app-backend": "^0.3.42",
"@backstage/plugin-auth-backend": "^0.18.0",
"@backstage/plugin-auth-node": "^0.2.11",
"@backstage/plugin-catalog-backend": "^1.7.2",
"@backstage/plugin-kubernetes-backend": "^0.9.3",
"@backstage/plugin-permission-common": "^0.7.3",
"@backstage/plugin-permission-node": "^0.7.5",
"@backstage/plugin-proxy-backend": "^0.2.36",
"@backstage/plugin-scaffolder-backend": "^1.11.0",
"@backstage/plugin-scaffolder-node": "^0.1.0",
"@backstage/plugin-search-backend": "^1.2.3",
"@backstage/plugin-search-backend-module-pg": "^0.5.3",
"@backstage/plugin-search-backend-node": "^1.1.3",
"@backstage/plugin-techdocs-backend": "^1.5.3",
"@kubernetes/client-node": "^0.18.1",
"app": "link:../app",
"better-sqlite3": "^8.0.0",
"dockerode": "^3.3.1",

View file

@ -32,6 +32,8 @@ import { PluginEnvironment } from './types';
import { ServerPermissionClient } from '@backstage/plugin-permission-node';
import { DefaultIdentityClient } from '@backstage/plugin-auth-node';
import kubernetes from './plugins/kubernetes';
function makeCreateEnv(config: Config) {
const root = getRootLogger();
const reader = UrlReaders.default({ logger: root, config });
@ -86,6 +88,8 @@ async function main() {
const searchEnv = useHotMemoize(module, () => createEnv('search'));
const appEnv = useHotMemoize(module, () => createEnv('app'));
const kubernetesEnv = useHotMemoize(module, () => createEnv('kubernetes'));
const apiRouter = Router();
apiRouter.use('/catalog', await catalog(catalogEnv));
apiRouter.use('/scaffolder', await scaffolder(scaffolderEnv));
@ -94,6 +98,8 @@ async function main() {
apiRouter.use('/proxy', await proxy(proxyEnv));
apiRouter.use('/search', await search(searchEnv));
apiRouter.use('/kubernetes', await kubernetes(kubernetesEnv));
// Add backends ABOVE this line; this 404 handler is the catch-all fallback
apiRouter.use(notFoundHandler());

View file

@ -0,0 +1,16 @@
import { KubernetesBuilder } from '@backstage/plugin-kubernetes-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
import { CatalogClient } from '@backstage/catalog-client';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
const catalogApi = new CatalogClient({discoveryApi: env.discovery});
const {router} = await KubernetesBuilder.createBuilder({
logger: env.logger,
config: env.config,
catalogApi,
}).build();
return router;
}

View file

@ -1,7 +1,9 @@
import { CatalogClient } from '@backstage/catalog-client';
import { createRouter } from '@backstage/plugin-scaffolder-backend';
import {createBuiltinActions, createRouter} from '@backstage/plugin-scaffolder-backend';
import { Router } from 'express';
import type { PluginEnvironment } from '../types';
import { ScmIntegrations } from '@backstage/integration';
import {createInvokeArgoAction} from './workflow-argo'
export default async function createPlugin(
env: PluginEnvironment,
@ -9,13 +11,25 @@ export default async function createPlugin(
const catalogClient = new CatalogClient({
discoveryApi: env.discovery,
});
const integrations = ScmIntegrations.fromConfig(env.config);
const builtInActions = createBuiltinActions({
integrations,
catalogClient,
config: env.config,
reader: env.reader,
});
console.log(`env.logger ${env.logger}`)
env.logger.info("HIIIII")
const actions = [...builtInActions, createInvokeArgoAction(env.config, env.logger)];
return await createRouter({
actions: actions,
logger: env.logger,
config: env.config,
database: env.database,
reader: env.reader,
catalogClient,
catalogClient: catalogClient,
identity: env.identity,
});
}

View file

@ -0,0 +1,111 @@
import {ActionContext, createTemplateAction} from "@backstage/plugin-scaffolder-node";
import { Config } from '@backstage/config';
import * as k8s from '@kubernetes/client-node';
import {Logger} from "winston";
import {HttpError} from "@kubernetes/client-node";
// export function createInvokeArgoAction() {
type argoInput = {
name: string
namespace: string
clusterName: string
}
export function createInvokeArgoAction(config: Config, logger: Logger) {
return createTemplateAction<argoInput>({
id: 'workflows:argo:invoke',
description:
'Append content to the end of the given file, it will create the file if it does not exist.',
schema: {
input: {
type: 'object',
required: ['name', 'namespace', 'clusterName'],
properties: {
name: {
title: 'Name',
description: 'Name of Argo workflow template',
type: 'string',
},
namespace: {
title: 'Namespace',
description: 'Namespace to run this workflow',
type: 'string',
},
clusterName: {
title: 'Cluster name',
description: 'Name of Cluster',
type: 'string',
},
},
},
output: {
type: 'object',
properties: {
ID: {
title: 'ID',
type: 'string',
},
},
},
},
async handler(ctx: ActionContext<argoInput>) {
logger.debug(`Invoked with ${ctx.input}`)
const c = config.getConfigArray("kubernetes.clusterLocatorMethods")
const cc = c.filter(function(val) {
return val.getString("type") === "config"
})
logger.info(`found ${cc.length} statically configured clusters`)
const clusters = new Array<Config>();
// this is shit
cc.forEach(function(conf ) {
const cl = conf.getConfigArray("clusters")
cl.forEach(function(val) {
if (val.getString("name") === ctx.input.clusterName) {
clusters.push(val)
}
})
})
if (clusters.length === 0 ) {
throw new Error("Cluster not found")
}
const targetCluster = clusters[0]
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.CoreV1Api)
logger.info("made client")
try {
const resp = await client.listNamespace()
logger.info(`response: ${resp.body}`)
} catch (error) {
if (error instanceof HttpError) {
logger.info(`error : ${error.response.statusCode} ${error.response.statusMessage}`)
throw new Error(`Failed to talk to the cluster: ${error.response.statusCode} ${error.response.statusMessage}`)
}
if (error instanceof Error) {
logger.error(`error while talking to cluster: ${error.name} ${error.message}`)
}
throw new Error("Unknown exception was encountered.")
}
}
}
)
}