wip
This commit is contained in:
parent
7e3bc0ac78
commit
3fa6a89fea
8 changed files with 175 additions and 7 deletions
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
16
packages/backend/src/plugins/kubernetes.ts
Normal file
16
packages/backend/src/plugins/kubernetes.ts
Normal 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;
|
||||
}
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
|
111
packages/backend/src/plugins/workflow-argo.ts
Normal file
111
packages/backend/src/plugins/workflow-argo.ts
Normal 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.")
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
Loading…
Reference in a new issue