wip
This commit is contained in:
parent
29fd61dde6
commit
66cd512a22
6 changed files with 1038 additions and 25 deletions
|
@ -45,11 +45,11 @@
|
|||
"@backstage/plugin-techdocs-react": "^1.1.8",
|
||||
"@backstage/plugin-user-settings": "^0.7.5",
|
||||
"@backstage/theme": "^0.4.1",
|
||||
"@cnoe-io/plugin-apache-spark": "0.1.1",
|
||||
"@cnoe-io/plugin-argo-workflows": "0.1.2",
|
||||
"@cnoe-io/plugin-apache-spark": "0.1.2",
|
||||
"@cnoe-io/plugin-argo-workflows": "0.1.3",
|
||||
"@cnoe-io/plugin-scaffolder-actions-frontend": "0.1.1",
|
||||
"@internal/plugin-workflows": "^0.1.0",
|
||||
"@internal/cnoe-ui-plugin": "^0.1.0",
|
||||
"@internal/plugin-workflows": "^0.1.0",
|
||||
"@material-ui/core": "^4.12.2",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@rjsf/core": "^5.8.1",
|
||||
|
|
|
@ -63,7 +63,6 @@ import {
|
|||
EntityArgoWorkflowsTemplateOverviewCard,
|
||||
isArgoWorkflowsAvailable,
|
||||
} from '@cnoe-io/plugin-argo-workflows';
|
||||
|
||||
import { ApacheSparkPage } from '@cnoe-io/plugin-apache-spark';
|
||||
|
||||
const techdocsContent = (
|
||||
|
@ -208,7 +207,9 @@ const websiteEntityPage = (
|
|||
</Grid>
|
||||
</Grid>
|
||||
</EntityLayout.Route>
|
||||
|
||||
<EntityLayout.Route path="/kubernetes" title="Kubernetes">
|
||||
<EntityKubernetesContent refreshIntervalMs={30000} />
|
||||
</EntityLayout.Route>
|
||||
<EntityLayout.Route path="/docs" title="Docs">
|
||||
{techdocsContent}
|
||||
</EntityLayout.Route>
|
||||
|
|
|
@ -31,7 +31,6 @@ import search from './plugins/search';
|
|||
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) {
|
||||
|
|
160
packages/backend/src/plugins/cnoe-kube.ts
Normal file
160
packages/backend/src/plugins/cnoe-kube.ts
Normal file
|
@ -0,0 +1,160 @@
|
|||
import { createTemplateAction } from '@backstage/plugin-scaffolder-node';
|
||||
import { dumpYaml } from '@kubernetes/client-node';
|
||||
import YAML from 'yaml';
|
||||
import { Config } from '@backstage/config';
|
||||
import { resolveSafeChildPath } from '@backstage/backend-common';
|
||||
import fs from 'fs-extra';
|
||||
import { executeShellCommand } from '@backstage/plugin-scaffolder-backend';
|
||||
|
||||
export const createKubernetesApply = (config: Config) => {
|
||||
return createTemplateAction<{
|
||||
manifestString?: string;
|
||||
manifestObject?: any;
|
||||
manifestPath?: string;
|
||||
namespaced: boolean;
|
||||
clusterName?: string;
|
||||
}>({
|
||||
id: 'cnoe:kubernetes:apply',
|
||||
schema: {
|
||||
input: {
|
||||
type: 'object',
|
||||
required: ['namespaced'],
|
||||
properties: {
|
||||
manifestString: {
|
||||
type: 'string',
|
||||
title: 'Manifest',
|
||||
description:
|
||||
'The manifest to apply in the cluster. Must be a string',
|
||||
},
|
||||
manifestObject: {
|
||||
type: 'object',
|
||||
title: 'Manifest',
|
||||
description:
|
||||
'The manifest to apply in the cluster. Must be an object',
|
||||
},
|
||||
manifestPath: {
|
||||
type: 'string',
|
||||
title: 'Path to the manifest file',
|
||||
description: 'The path to the manifest file.',
|
||||
},
|
||||
namespaced: {
|
||||
type: 'boolean',
|
||||
title: 'Namespaced',
|
||||
description: 'Whether the API is namespaced or not',
|
||||
},
|
||||
clusterName: {
|
||||
type: 'string',
|
||||
title: 'Cluster Name',
|
||||
description: 'The name of the cluster to apply this',
|
||||
},
|
||||
},
|
||||
},
|
||||
output: {
|
||||
type: 'object',
|
||||
title: 'Returned object',
|
||||
description:
|
||||
'The object returned by Kubernetes by performing this operation',
|
||||
},
|
||||
},
|
||||
async handler(ctx) {
|
||||
let obj: any;
|
||||
if (ctx.input.manifestString) {
|
||||
obj = YAML.parse(ctx.input.manifestString);
|
||||
} else if (ctx.input.manifestObject) {
|
||||
obj = ctx.input.manifestObject;
|
||||
} else {
|
||||
const filePath = resolveSafeChildPath(
|
||||
ctx.workspacePath,
|
||||
ctx.input.manifestPath!,
|
||||
);
|
||||
const fileContent = fs.readFileSync(filePath, 'utf8');
|
||||
obj = YAML.parse(fileContent);
|
||||
}
|
||||
|
||||
if (ctx.input.clusterName) {
|
||||
// Supports SA token authentication only
|
||||
const targetCluster = getClusterConfig(ctx.input.clusterName!, config);
|
||||
const confFile = {
|
||||
apiVersion: 'v1',
|
||||
kind: 'Config',
|
||||
'current-context': ctx.input.clusterName,
|
||||
contexts: [
|
||||
{
|
||||
name: ctx.input.clusterName,
|
||||
context: {
|
||||
cluster: ctx.input.clusterName,
|
||||
user: ctx.input.clusterName,
|
||||
},
|
||||
},
|
||||
],
|
||||
clusters: [
|
||||
{
|
||||
name: ctx.input.clusterName,
|
||||
cluster: {
|
||||
'certificate-authority-data': targetCluster.getString('caData'),
|
||||
server: targetCluster.getString('url'),
|
||||
},
|
||||
},
|
||||
],
|
||||
users: [
|
||||
{
|
||||
name: ctx.input.clusterName,
|
||||
user: {
|
||||
token: targetCluster.getString('serviceAccountToken'),
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
const confString = dumpYaml(confFile);
|
||||
const confFilePath = resolveSafeChildPath(ctx.workspacePath, 'config');
|
||||
fs.writeFileSync(confFilePath, confString, {
|
||||
encoding: 'utf8',
|
||||
mode: '600',
|
||||
});
|
||||
const manifestPath = resolveSafeChildPath(
|
||||
ctx.workspacePath,
|
||||
ctx.input.manifestPath!,
|
||||
);
|
||||
if (obj.metadata.generateName !== undefined) {
|
||||
await executeShellCommand({
|
||||
command: 'kubectl',
|
||||
args: ['--kubeconfig', confFilePath, 'create', '-f', manifestPath],
|
||||
logStream: ctx.logStream,
|
||||
});
|
||||
return;
|
||||
}
|
||||
await executeShellCommand({
|
||||
command: 'kubectl',
|
||||
args: ['--kubeconfig', confFilePath, 'apply', '-f', manifestPath],
|
||||
logStream: ctx.logStream,
|
||||
});
|
||||
return;
|
||||
}
|
||||
return;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Finds the first cluster that matches the given name.
|
||||
function getClusterConfig(name: string, config: Config): Config {
|
||||
const clusterConfigs = config
|
||||
.getConfigArray('kubernetes.clusterLocatorMethods')
|
||||
.filter((val: Config) => {
|
||||
return val.getString('type') === 'config';
|
||||
});
|
||||
|
||||
const clusters = new Array<Config>();
|
||||
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`);
|
||||
}
|
||||
return clusters[0];
|
||||
}
|
|
@ -22,11 +22,13 @@ import {
|
|||
createJsonJSONataTransformAction,
|
||||
} from '@roadiehq/scaffolder-backend-module-utils';
|
||||
import {
|
||||
createKubernetesApply,
|
||||
// createKubernetesApply,
|
||||
createSanitizeResource,
|
||||
createVerifyDependency,
|
||||
} from '@cnoe-io/plugin-scaffolder-actions';
|
||||
|
||||
import { createKubernetesApply } from './cnoe-kube';
|
||||
|
||||
export default async function createPlugin(
|
||||
env: PluginEnvironment,
|
||||
): Promise<Router> {
|
||||
|
|
Loading…
Reference in a new issue