This commit is contained in:
Manabu Mccloskey 2023-09-11 14:51:16 -07:00
parent 29fd61dde6
commit 66cd512a22
6 changed files with 1038 additions and 25 deletions

View file

@ -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",

View file

@ -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>

View file

@ -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) {

View 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];
}

View file

@ -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> {

887
yarn.lock

File diff suppressed because it is too large Load diff