upgrade to backstage 1.26.5 (#21)

Signed-off-by: Manabu McCloskey <manabu.mccloskey@gmail.com>
This commit is contained in:
Manabu McCloskey 2024-06-17 09:45:36 -07:00 committed by GitHub
parent b8e4f08914
commit c2ff2abd11
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 4185 additions and 2644 deletions

View file

@ -1,6 +1,6 @@
# CNOE [Backstage](https://backstage.io)
# CNOE Backstge
This repository contains code for the Backstage images used by the CNOE stacks.
This repository contains code for the [Backstage](https://backstage.io) images used by the CNOE stacks.
## Container Images
@ -98,6 +98,23 @@ Data:
username : admin
```
#### Update Gitea Credentials
Gitea admin passwords are generated on each fresh installation as well. To obtain your password, run: `./idpbuilder get secrets -p argocd`.
Then update [this line](https://github.com/cnoe-io/backstage-app/blob/9ee3514e51c1a354b7fe85a90117faf8328bfa0b/app-config.yaml#L40) and [this line](https://github.com/cnoe-io/backstage-app/blob/9ee3514e51c1a354b7fe85a90117faf8328bfa0b/app-config.yaml#L44).
For example:
```bash
$ ./idpbuilder get secrets -p gitea
---------------------------
Name: gitea-credential
Namespace: gitea
Data:
password : abc
username : giteaAdmin
````
### Start Backstage processes

View file

@ -31,17 +31,16 @@ backend:
client: better-sqlite3
connection: ':memory:'
# workingDirectory: /tmp # Use this to configure a working directory for the scaffolder, defaults to the OS temp-dir
integrations:
gitea:
- baseUrl: https://gitea.cnoe.localtest.me:8443
host: gitea.cnoe.localtest.me:8443
- baseUrl: https://cnoe.localtest.me:8443/gitea
host: cnoe.localtest.me:8443
username: giteaAdmin
password: giteaPassword
- baseUrl: https://gitea.cnoe.localtest.me
host: gitea.cnoe.localtest.me:8443
password: ${GITEA_PASSWORD}
- baseUrl: https://cnoe.localtest.me/gitea
host: cnoe.localtest.me
username: giteaAdmin
password: giteaPassword
password: ${GITEA_PASSWORD}
proxy:
### Example for how to add a proxy endpoint for the frontend.
@ -61,16 +60,16 @@ techdocs:
runIn: 'docker' # Alternatives - 'local'
publisher:
type: 'local' # Alternatives - 'googleGcs' or 'awsS3'. Read documentation for using alternatives.
auth:
# see https://backstage.io/docs/auth/ to learn about auth providers
environment: local # set this to development to enable SSO
session:
secret: abcdfkjalskdfjkla
providers:
guest: {}
keycloak-oidc:
development:
metadataUrl: https://keycloak.cnoe.localtest.me:8443/realms/cnoe/.well-known/openid-configuration
metadataUrl: https://cnoe.localtest.me:8443/keycloak/realms/cnoe/.well-known/openid-configuration
clientId: backstage
clientSecret: ${KEYCLOAK_CLIENT_SECRET}
scope: 'openid profile email groups'
@ -90,8 +89,7 @@ catalog:
- allow: [ Component, System, API, Resource, Location, Template ]
locations:
- type: url
target: https://gitea.cnoe.localtest.me:8443/giteaAdmin/entities/src/branch/main/catalog-info.yaml
target: https://cnoe.localtest.me:8443/gitea/giteaAdmin/idpbuilder-localdev-backstage-templates-entities/src/branch/main/catalog-info.yaml
# # Local example template
# - type: file
# target: ../../examples/template/template.yaml
@ -129,10 +127,10 @@ argocd:
appLocatorMethods:
- type: 'config'
instances:
- name: in-cluster
url: https://argocd.cnoe.localtest.me:8443
- name: local
url: https://cnoe.localtest.me:8443/argocd
username: admin
# replace with your argocd password e.g. kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
password: ${ARGOCD_ADMIN_PASSWORD}
argoWorkflows:
baseUrl: https://argo.cnoe.localtest.me:8443
baseUrl: https://cnoe.localtest.me:8443/argo-workflows

View file

@ -1,3 +1,3 @@
{
"version": "1.22.1"
"version": "1.26.5"
}

View file

@ -31,8 +31,8 @@
]
},
"devDependencies": {
"@backstage/cli": "^0.25.1",
"@backstage/e2e-test-utils": "^0.1.0",
"@backstage/cli": "^0.26.4",
"@backstage/e2e-test-utils": "^0.1.1",
"@playwright/test": "^1.32.3",
"@spotify/prettier-config": "^12.0.0",
"concurrently": "^8.0.0",

View file

@ -14,33 +14,33 @@
"lint": "backstage-cli package lint"
},
"dependencies": {
"@backstage/app-defaults": "~1.4.7",
"@backstage/catalog-model": "~1.4.3",
"@backstage/cli": "~0.25.1",
"@backstage/core-app-api": "~1.11.3",
"@backstage/core-components": "~0.13.10",
"@backstage/core-plugin-api": "~1.8.2",
"@backstage/integration-react": "~1.1.23",
"@backstage/plugin-api-docs": "~0.10.3",
"@backstage/plugin-catalog": "~1.16.1",
"@backstage/plugin-catalog-common": "~1.0.20",
"@backstage/plugin-catalog-graph": "~0.3.3",
"@backstage/plugin-catalog-import": "~0.10.5",
"@backstage/plugin-catalog-react": "~1.9.3",
"@backstage/plugin-github-actions": "~0.6.10",
"@backstage/plugin-home": "~0.6.1",
"@backstage/plugin-kubernetes": "~0.11.4",
"@backstage/plugin-org": "~0.6.19",
"@backstage/plugin-permission-react": "~0.4.19",
"@backstage/plugin-scaffolder": "~1.17.1",
"@backstage/plugin-search": "~1.4.5",
"@backstage/plugin-search-react": "~1.7.5",
"@backstage/plugin-tech-radar": "~0.6.12",
"@backstage/plugin-techdocs": "~1.9.3",
"@backstage/plugin-techdocs-module-addons-contrib": "~1.1.4",
"@backstage/plugin-techdocs-react": "~1.1.15",
"@backstage/plugin-user-settings": "~0.8.0",
"@backstage/theme": "~0.5.0",
"@backstage/app-defaults": "^1.5.4",
"@backstage/catalog-model": "^1.4.5",
"@backstage/cli": "^0.26.4",
"@backstage/core-app-api": "^1.12.4",
"@backstage/core-components": "^0.14.6",
"@backstage/core-plugin-api": "^1.9.2",
"@backstage/integration-react": "^1.1.26",
"@backstage/plugin-api-docs": "^0.11.4",
"@backstage/plugin-catalog": "^1.19.0",
"@backstage/plugin-catalog-common": "^1.0.22",
"@backstage/plugin-catalog-graph": "^0.4.4",
"@backstage/plugin-catalog-import": "^0.10.10",
"@backstage/plugin-catalog-react": "^1.11.3",
"@backstage/plugin-github-actions": "^0.6.16",
"@backstage/plugin-home": "^0.7.3",
"@backstage/plugin-kubernetes": "^0.11.9",
"@backstage/plugin-org": "^0.6.24",
"@backstage/plugin-permission-react": "^0.4.22",
"@backstage/plugin-scaffolder": "^1.19.3",
"@backstage/plugin-search": "^1.4.10",
"@backstage/plugin-search-react": "^1.7.10",
"@backstage/plugin-tech-radar": "^0.7.4",
"@backstage/plugin-techdocs": "^1.10.4",
"@backstage/plugin-techdocs-module-addons-contrib": "^1.1.9",
"@backstage/plugin-techdocs-react": "^1.2.3",
"@backstage/plugin-user-settings": "^0.8.5",
"@backstage/theme": "^0.5.3",
"@internal/plugin-apache-spark": "^0.1.0",
"@internal/plugin-argo-workflows": "^0.1.0",
"@internal/plugin-cnoe-ui": "^0.1.0",
@ -55,7 +55,7 @@
"react-use": "^17.2.4"
},
"devDependencies": {
"@backstage/test-utils": "^1.4.7",
"@backstage/test-utils": "^1.5.4",
"@playwright/test": "^1.32.3",
"@testing-library/dom": "^9.0.0",
"@testing-library/jest-dom": "^6.0.0",

View file

@ -161,3 +161,5 @@ export default app.createRoot(
</>,
);

View file

@ -13,7 +13,7 @@ import {OAuth2} from "@backstage/core-app-api";
export const keycloakOIDCAuthApiRef: ApiRef<
OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
> = createApiRef({
id: 'auth.keycloak-oidc-provider',
id: 'auth.keyloak-oidc',
});
export const apis: AnyApiFactory[] = [
createApiFactory({

View file

@ -16,34 +16,40 @@
"build-image": "docker build ../.. -f Dockerfile --tag backstage"
},
"dependencies": {
"@backstage/backend-common": "~0.20.1",
"@backstage/backend-tasks": "~0.5.14",
"@backstage/catalog-client": "~1.5.2",
"@backstage/catalog-model": "~1.4.3",
"@backstage/config": "~1.1.1",
"@backstage/errors": "~1.2.3",
"@backstage/integration": "~1.8.0",
"@backstage/plugin-app-backend": "~0.3.57",
"@backstage/plugin-auth-backend": "~0.20.3",
"@backstage/plugin-auth-node": "~0.4.3",
"@backstage/plugin-catalog-backend": "~1.16.1",
"@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "~0.1.6",
"@backstage/plugin-kubernetes-backend": "~0.14.1",
"@backstage/plugin-permission-common": "~0.7.12",
"@backstage/plugin-permission-node": "~0.7.20",
"@backstage/plugin-proxy-backend": "~0.4.7",
"@backstage/plugin-scaffolder-backend": "~1.20.0",
"@backstage/plugin-scaffolder-node": "~0.2.9",
"@backstage/plugin-search-backend": "~1.4.9",
"@backstage/plugin-search-backend-module-catalog": "~0.1.13",
"@backstage/plugin-search-backend-module-pg": "~0.5.18",
"@backstage/plugin-search-backend-module-techdocs": "~0.1.13",
"@backstage/plugin-search-backend-node": "~1.2.13",
"@backstage/plugin-techdocs-backend": "~1.9.2",
"@backstage/types": "~1.1.1",
"@backstage/backend-common": "^0.21.7",
"@backstage/backend-defaults": "^0.2.18",
"@backstage/backend-plugin-api": "^0.6.18",
"@backstage/backend-tasks": "^0.5.22",
"@backstage/catalog-client": "^1.6.4",
"@backstage/catalog-model": "^1.4.5",
"@backstage/config": "^1.2.0",
"@backstage/errors": "^1.2.4",
"@backstage/integration": "^1.10.0",
"@backstage/plugin-app-backend": "^0.3.65",
"@backstage/plugin-auth-backend": "^0.22.5",
"@backstage/plugin-auth-backend-module-guest-provider": "^0.1.4",
"@backstage/plugin-auth-backend-module-oidc-provider": "^0.1.9",
"@backstage/plugin-auth-node": "^0.4.12",
"@backstage/plugin-catalog-backend": "^1.21.1",
"@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "^0.1.15",
"@backstage/plugin-kubernetes-backend": "^0.17.0",
"@backstage/plugin-permission-common": "^0.7.13",
"@backstage/plugin-permission-node": "^0.7.28",
"@backstage/plugin-proxy-backend": "^0.4.15",
"@backstage/plugin-scaffolder-backend": "^1.22.5",
"@backstage/plugin-scaffolder-backend-module-gitea": "^0.1.8",
"@backstage/plugin-scaffolder-backend-module-github": "^0.2.8",
"@backstage/plugin-scaffolder-node": "^0.4.4",
"@backstage/plugin-search-backend": "^1.5.7",
"@backstage/plugin-search-backend-module-catalog": "^0.1.23",
"@backstage/plugin-search-backend-module-pg": "^0.5.26",
"@backstage/plugin-search-backend-module-techdocs": "^0.1.22",
"@backstage/plugin-search-backend-node": "^1.2.21",
"@backstage/plugin-techdocs-backend": "^1.10.4",
"@backstage/types": "^1.1.1",
"@kubernetes/client-node": "~0.20.0",
"@roadiehq/backstage-plugin-argo-cd-backend": "~2.14.0",
"@roadiehq/scaffolder-backend-module-utils": "~1.13.1",
"@roadiehq/backstage-plugin-argo-cd-backend": "3.0.2",
"@roadiehq/scaffolder-backend-module-utils": "^1.17.0",
"app": "link:../app",
"better-sqlite3": "^9.0.0",
"dockerode": "^3.3.1",
@ -55,7 +61,7 @@
"winston": "^3.2.1"
},
"devDependencies": {
"@backstage/cli": "^0.25.1",
"@backstage/cli": "^0.26.4",
"@types/dockerode": "^3.3.0",
"@types/express": "^4.17.6",
"@types/express-serve-static-core": "^4.17.5",

View file

@ -1,123 +1,36 @@
/*
* Hi!
*
* Note that this is an EXAMPLE Backstage backend. Please check the README.
*
* Happy hacking!
*/
import { createBackend } from '@backstage/backend-defaults';
import { authModuleKeycloakOIDCProvider } from './plugins/auth';
import { cnoeScaffolderActions } from './plugins/scaffolder';
import { legacyPlugin } from '@backstage/backend-common';
import Router from 'express-promise-router';
import {
createServiceBuilder,
loadBackendConfig,
getRootLogger,
useHotMemoize,
notFoundHandler,
CacheManager,
DatabaseManager,
HostDiscovery,
UrlReaders,
ServerTokenManager,
} from '@backstage/backend-common';
import { TaskScheduler } from '@backstage/backend-tasks';
import { Config } from '@backstage/config';
import app from './plugins/app';
import auth from './plugins/auth';
import catalog from './plugins/catalog';
import scaffolder from './plugins/scaffolder';
import proxy from './plugins/proxy';
import techdocs from './plugins/techdocs';
import search from './plugins/search';
import { PluginEnvironment } from './types';
import { ServerPermissionClient } from '@backstage/plugin-permission-node';
import { DefaultIdentityClient } from '@backstage/plugin-auth-node';
const backend = createBackend();
import kubernetes from './plugins/kubernetes';
import argocd from './plugins/argocd';
function makeCreateEnv(config: Config) {
const root = getRootLogger();
const reader = UrlReaders.default({ logger: root, config });
const discovery = HostDiscovery.fromConfig(config);
const cacheManager = CacheManager.fromConfig(config);
const databaseManager = DatabaseManager.fromConfig(config, { logger: root });
const tokenManager = ServerTokenManager.noop();
const taskScheduler = TaskScheduler.fromConfig(config, { databaseManager });
// core plugins
backend.add(import('@backstage/plugin-app-backend/alpha'));
backend.add(import('@backstage/plugin-catalog-backend/alpha'));
backend.add(import('@backstage/plugin-proxy-backend/alpha'));
backend.add(import('@backstage/plugin-techdocs-backend/alpha'));
// auth plugins
backend.add(import('@backstage/plugin-auth-backend'));
backend.add(import('@backstage/plugin-auth-backend-module-guest-provider'));
// scaffolder plugins
backend.add(import('@backstage/plugin-scaffolder-backend/alpha'));
backend.add(
import('@backstage/plugin-catalog-backend-module-scaffolder-entity-model'),
);
backend.add(import('@backstage/plugin-scaffolder-backend-module-github'));
// search plugins
backend.add(import('@backstage/plugin-search-backend/alpha'));
backend.add(import('@backstage/plugin-search-backend-module-catalog/alpha'));
backend.add(import('@backstage/plugin-search-backend-module-techdocs/alpha'));
// other @backstage plugins
backend.add(import('@backstage/plugin-kubernetes-backend/alpha'));
// non-core plugins
// roadie plugins
backend.add(import('@roadiehq/scaffolder-backend-module-utils/new-backend'));
backend.add(legacyPlugin('argocd', import('./plugins/argocd')));
// cnoe plugins
backend.add(authModuleKeycloakOIDCProvider);
backend.add(cnoeScaffolderActions);
const identity = DefaultIdentityClient.create({
discovery,
});
const permissions = ServerPermissionClient.fromConfig(config, {
discovery,
tokenManager,
});
root.info(`Created UrlReader ${reader}`);
return (plugin: string): PluginEnvironment => {
const logger = root.child({ type: 'plugin', plugin });
const database = databaseManager.forPlugin(plugin);
const cache = cacheManager.forPlugin(plugin);
const scheduler = taskScheduler.forPlugin(plugin);
return {
logger,
database,
cache,
config,
reader,
discovery,
tokenManager,
scheduler,
permissions,
identity,
};
};
}
async function main() {
const config = await loadBackendConfig({
argv: process.argv,
logger: getRootLogger(),
});
const createEnv = makeCreateEnv(config);
const catalogEnv = useHotMemoize(module, () => createEnv('catalog'));
const scaffolderEnv = useHotMemoize(module, () => createEnv('scaffolder'));
const authEnv = useHotMemoize(module, () => createEnv('auth'));
const proxyEnv = useHotMemoize(module, () => createEnv('proxy'));
const techdocsEnv = useHotMemoize(module, () => createEnv('techdocs'));
const searchEnv = useHotMemoize(module, () => createEnv('search'));
const appEnv = useHotMemoize(module, () => createEnv('app'));
const kubernetesEnv = useHotMemoize(module, () => createEnv('kubernetes'));
const argocdEnv = useHotMemoize(module, () => createEnv('argocd'));
const apiRouter = Router();
apiRouter.use('/catalog', await catalog(catalogEnv));
apiRouter.use('/scaffolder', await scaffolder(scaffolderEnv));
apiRouter.use('/auth', await auth(authEnv));
apiRouter.use('/techdocs', await techdocs(techdocsEnv));
apiRouter.use('/proxy', await proxy(proxyEnv));
apiRouter.use('/search', await search(searchEnv));
apiRouter.use('/kubernetes', await kubernetes(kubernetesEnv));
apiRouter.use('/argocd', await argocd(argocdEnv));
// Add backends ABOVE this line; this 404 handler is the catch-all fallback
apiRouter.use(notFoundHandler());
const service = createServiceBuilder(module)
.loadConfig(config)
.addRouter('/api', apiRouter)
.addRouter('', await app(appEnv));
await service.start().catch(err => {
console.log(err);
process.exit(1);
});
}
module.hot?.accept();
main().catch(error => {
console.error('Backend failed to start up', error);
process.exit(1);
});
backend.start();

View file

@ -1,14 +0,0 @@
import { createRouter } from '@backstage/plugin-app-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
return await createRouter({
logger: env.logger,
config: env.config,
database: env.database,
appPackageName: 'app',
});
}

View file

@ -1,7 +1,7 @@
import {Config} from "@backstage/config";
import {createTemplateAction} from "@backstage/plugin-scaffolder-node";
import {examples} from "./gitea-actions";
import {Logger} from "winston";
import { Config } from '@backstage/config';
import { createTemplateAction } from '@backstage/plugin-scaffolder-node';
import { examples } from './gitea-actions';
import { Logger } from 'winston';
import { ArgoService } from '@roadiehq/backstage-plugin-argo-cd-backend';
@ -9,16 +9,13 @@ import { createRouter } from '@roadiehq/backstage-plugin-argo-cd-backend';
import { PluginEnvironment } from '../types';
export default async function createPlugin({
logger,
config,
}: PluginEnvironment) {
logger,
config,
}: PluginEnvironment) {
return await createRouter({ logger, config });
}
export function createArgoCDApp(options: {
config: Config;
logger: Logger
}) {
export function createArgoCDApp(options: { config: Config; logger: Logger }) {
const { config, logger } = options;
return createTemplateAction<{
@ -28,16 +25,22 @@ export function createArgoCDApp(options: {
argoInstance: string;
path: string;
labelValue?: string;
appNamespace: string
appNamespace: string;
}>({
id: 'cnoe:create-argocd-app',
description:
'creates argocd app',
description: 'creates argocd app',
examples,
schema: {
input: {
type: 'object',
required: ['repoUrl', 'projectName', 'appName', 'argoInstance', 'path', 'appNamespace'],
required: [
'repoUrl',
'projectName',
'appName',
'argoInstance',
'path',
'appNamespace',
],
properties: {
repoUrl: {
title: 'Repository Location',
@ -66,14 +69,12 @@ export function createArgoCDApp(options: {
labelValue: {
title: 'for argocd plugin to locate this app',
type: 'string',
}
},
},
},
output: {
},
output: {},
},
async handler(ctx) {
const {
repoUrl,
projectName,
@ -81,7 +82,7 @@ export function createArgoCDApp(options: {
argoInstance,
path,
labelValue,
appNamespace
appNamespace,
} = ctx.input;
const argoUserName =
@ -130,7 +131,7 @@ export function createArgoCDApp(options: {
sourceRepo: repoUrl,
sourcePath: path,
labelValue: labelValue ? labelValue : appName,
})
});
},
});
}

View file

@ -1,57 +1,67 @@
import {
createRouter,
providers,
defaultAuthProviderFactories,
} from '@backstage/plugin-auth-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
import {
DEFAULT_NAMESPACE,
stringifyEntityRef,
} from '@backstage/catalog-model';
import { JsonArray } from '@backstage/types';
import { createBackendModule } from '@backstage/backend-plugin-api';
import {
authProvidersExtensionPoint,
createOAuthProviderFactory,
OAuthAuthenticatorResult,
} from '@backstage/plugin-auth-node';
import {
oidcAuthenticator,
OidcAuthResult,
} from '@backstage/plugin-auth-backend-module-oidc-provider';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
const opts = {
logger: env.logger,
config: env.config,
database: env.database,
discovery: env.discovery,
tokenManager: env.tokenManager,
providerFactories: {
...defaultAuthProviderFactories,
},
};
const envName = env.config
.getOptionalConfig('auth')
?.getOptionalString('auth');
if (envName === 'local') {
return await createRouter(opts);
}
const keycloakAuth = (opts.providerFactories['keycloak-oidc'] =
providers.oidc.create({
signIn: {
resolver(info, ctx) {
const userRef = stringifyEntityRef({
kind: 'User',
name: info.result.userinfo.sub,
namespace: DEFAULT_NAMESPACE,
});
return ctx.issueToken({
claims: {
sub: userRef,
ent: [userRef],
groups: (info.result.userinfo.groups as JsonArray) || [],
},
});
},
export const authModuleKeycloakOIDCProvider = createBackendModule({
pluginId: 'auth',
moduleId: 'keycloak-oidc',
register(reg) {
reg.registerInit({
deps: {
providers: authProvidersExtensionPoint,
},
}));
opts.providerFactories['keycloak-oidc'] = keycloakAuth;
async init({ providers }) {
providers.registerProvider({
providerId: 'keycloak-oidc',
factory: createOAuthProviderFactory({
authenticator: oidcAuthenticator,
profileTransform: async (
input: OAuthAuthenticatorResult<OidcAuthResult>,
) => ({
profile: {
email: input.fullProfile.userinfo.email,
picture: input.fullProfile.userinfo.picture,
displayName: input.fullProfile.userinfo.name,
},
}),
async signInResolver(info, ctx) {
const { profile } = info;
if (!profile.displayName) {
throw new Error(
'Login failed, user profile does not contain a valid name',
);
}
const userRef = stringifyEntityRef({
kind: 'User',
name: info.profile.displayName!,
namespace: DEFAULT_NAMESPACE,
});
return await createRouter(opts);
}
return ctx.issueToken({
claims: {
sub: userRef,
ent: [userRef],
groups:
(info.result.fullProfile.userinfo.groups as JsonArray) ||
[],
},
});
},
}),
});
},
});
},
});

View file

@ -1,14 +0,0 @@
import { CatalogBuilder } from '@backstage/plugin-catalog-backend';
import { ScaffolderEntitiesProcessor } from '@backstage/plugin-catalog-backend-module-scaffolder-entity-model';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
const builder = await CatalogBuilder.create(env);
builder.addProcessor(new ScaffolderEntitiesProcessor());
const { processingEngine, router } = await builder.build();
await processingEngine.start();
return router;
}

View file

@ -1,16 +1,16 @@
// this is necessary until https://github.com/backstage/backstage/pull/21890/ is merged and released.
import { InputError } from '@backstage/errors';
import { Config } from '@backstage/config';
import {
getGiteaRequestOptions,
GiteaIntegrationConfig,
ScmIntegrationRegistry, ScmIntegrations,
ScmIntegrationRegistry,
ScmIntegrations,
} from '@backstage/integration';
import {
createTemplateAction,
getRepoSourceDirectory,
initRepoAndPush,
TemplateExample
TemplateExample,
} from '@backstage/plugin-scaffolder-node';
import crypto from 'crypto';
import yaml from 'yaml';
@ -290,7 +290,6 @@ const checkGiteaOrg = async (
}
};
const createGiteaProject = async (
config: GiteaIntegrationConfig,
options: {
@ -357,8 +356,6 @@ const createGiteaProject = async (
);
}
}
};
const generateCommitMessage = (
@ -508,7 +505,6 @@ export function createPublishGiteaAction(options: {
},
},
async handler(ctx) {
const {
repoUrl,
description,
@ -555,7 +551,7 @@ export function createPublishGiteaAction(options: {
: config.getOptionalString('scaffolder.defaultAuthor.email'),
};
// The owner to be used should be either the org name or user authenticated with the gitea server
const repoOwner = owner ? owner: username
const repoOwner = owner ? owner : username;
const remoteUrl = `${integrationConfig.config.baseUrl}/${repoOwner}/${repo}.git`;
const commitResult = await initRepoAndPush({
dir: getRepoSourceDirectory(ctx.workspacePath, sourcePath),
@ -569,7 +565,8 @@ export function createPublishGiteaAction(options: {
// Check if the gitea repo URL is available before to exit
const operationTimeLimit = 5000; // 20 seconds
const sleep = (ms: number | undefined) => new Promise(r => setTimeout(r, ms));
const sleep = (ms: number | undefined) =>
new Promise(r => setTimeout(r, ms));
await sleep(operationTimeLimit);
// await checkAvailabilityGiteaRepository(
// integrationConfig.config, {
@ -601,5 +598,3 @@ export function createPublishGiteaAction(options: {
},
});
}

View file

@ -1,4 +1,7 @@
import { createTemplateAction, executeShellCommand} from '@backstage/plugin-scaffolder-node';
import {
createTemplateAction,
executeShellCommand,
} from '@backstage/plugin-scaffolder-node';
import { dumpYaml } from '@kubernetes/client-node';
import yaml from 'js-yaml';
import { Config } from '@backstage/config';
@ -57,9 +60,12 @@ export const createKubernetesApply = (config: Config) => {
},
async handler(ctx) {
let obj: any;
let manifestPath = resolveSafeChildPath(ctx.workspacePath, 'to-be-applied.yaml');
let manifestPath = resolveSafeChildPath(
ctx.workspacePath,
'to-be-applied.yaml',
);
if (ctx.input.manifestString) {
obj = yaml.load(ctx.input.manifestString)
obj = yaml.load(ctx.input.manifestString);
fs.writeFileSync(manifestPath, ctx.input.manifestString, {
encoding: 'utf8',
mode: '600',
@ -76,7 +82,7 @@ export const createKubernetesApply = (config: Config) => {
ctx.input.manifestPath!,
);
const fileContent = fs.readFileSync(filePath, 'utf8');
manifestPath = filePath
manifestPath = filePath;
obj = yaml.load(fileContent);
}
@ -100,10 +106,13 @@ export const createKubernetesApply = (config: Config) => {
{
name: ctx.input.clusterName,
cluster: {
'certificate-authority-data': targetCluster.getOptionalString('caData'),
'certificate-authority': targetCluster.getOptionalString('caFile'),
'certificate-authority-data':
targetCluster.getOptionalString('caData'),
'certificate-authority':
targetCluster.getOptionalString('caFile'),
server: targetCluster.getString('url'),
'insecure-skip-tls-verify': !!targetCluster.getOptionalBoolean('skipTLSVerify'),
'insecure-skip-tls-verify':
!!targetCluster.getOptionalBoolean('skipTLSVerify'),
},
},
],
@ -116,14 +125,16 @@ export const createKubernetesApply = (config: Config) => {
},
],
};
if (!confFile.clusters[0].cluster["insecure-skip-tls-verify"]) {
let caDataRaw = targetCluster.getOptionalString('caData')
if (!confFile.clusters[0].cluster['insecure-skip-tls-verify']) {
let caDataRaw = targetCluster.getOptionalString('caData');
if (caDataRaw?.startsWith('-----BEGIN CERTIFICATE-----')) {
caDataRaw = Buffer.from(targetCluster.getString('caData'), 'utf8').toString(
'base64',
);
caDataRaw = Buffer.from(
targetCluster.getString('caData'),
'utf8',
).toString('base64');
}
confFile.clusters[0].cluster['certificate-authority-data'] = caDataRaw
confFile.clusters[0].cluster['certificate-authority-data'] =
caDataRaw;
}
const confString = dumpYaml(confFile);
const confFilePath = resolveSafeChildPath(ctx.workspacePath, 'config');
@ -156,7 +167,7 @@ export const createKubernetesApply = (config: Config) => {
});
return;
}
throw new Error("please specify a valid cluster name")
throw new Error('please specify a valid cluster name');
},
});
};

View file

@ -1,18 +0,0 @@
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,
permissions: env.permissions,
}).build();
return router;
}

View file

@ -30,10 +30,10 @@ export const createSanitizeResource = () => {
properties: {
sanitized: {
type: 'string',
description: 'The sanitized yaml string'
}
}
}
description: 'The sanitized yaml string',
},
},
},
},
async handler(ctx) {
const obj = yaml.load(ctx.input.document);

View file

@ -1,92 +1,44 @@
import { CatalogClient } from '@backstage/catalog-client';
import {createBuiltinActions, createRouter} from '@backstage/plugin-scaffolder-backend';
import { Router } from 'express';
import type { PluginEnvironment } from '../types';
import { ScmIntegrations } from '@backstage/integration';
import {createPublishGiteaAction} from "./gitea-actions";
import {createArgoCDApp} from "./argocd";
import { createPublishGiteaAction } from './gitea-actions';
import {
createZipAction,
createSleepAction,
createWriteFileAction,
createAppendFileAction,
createMergeJSONAction,
createMergeAction,
createParseFileAction,
createSerializeYamlAction,
createSerializeJsonAction,
createJSONataAction,
createYamlJSONataTransformAction,
createJsonJSONataTransformAction,
createReplaceInFileAction
} from '@roadiehq/scaffolder-backend-module-utils';
import {createKubernetesApply} from "./k8s-apply";
import {createSanitizeResource} from "./sanitize";
import {createVerifyDependency} from "./verify";
coreServices,
createBackendModule,
} from '@backstage/backend-plugin-api';
import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha';
import { createArgoCDApp } from './argocd';
import { getRootLogger } from '@backstage/backend-common';
import { createKubernetesApply } from './k8s-apply';
import { createSanitizeResource } from './sanitize';
import { createVerifyDependency } from './verify';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
const catalogClient = new CatalogClient({
discoveryApi: env.discovery,
});
export const cnoeScaffolderActions = createBackendModule({
pluginId: 'scaffolder',
moduleId: 'cnoe-actions',
register(env) {
env.registerInit({
deps: {
scaffolder: scaffolderActionsExtensionPoint,
config: coreServices.rootConfig,
},
async init({ scaffolder, config }) {
const integrations = ScmIntegrations.fromConfig(config);
const logger = getRootLogger();
const integrations = ScmIntegrations.fromConfig(env.config);
const builtInActions = createBuiltinActions({
integrations,
catalogClient,
config: env.config,
reader: env.reader,
});
const options = {
integrations: integrations,
config: env.config,
}
const argocdOptions = {
config: env.config,
logger: env.logger
}
const cnoeActions = [
createPublishGiteaAction(options),
createArgoCDApp(argocdOptions),
createKubernetesApply(env.config),
createSanitizeResource(),
createVerifyDependency()
]
const roadieUtilActions = [
createZipAction(),
createSleepAction(),
createWriteFileAction(),
createAppendFileAction(),
createMergeJSONAction({}),
createMergeAction(),
createParseFileAction(),
createSerializeYamlAction(),
createSerializeJsonAction(),
createJSONataAction(),
createYamlJSONataTransformAction(),
createJsonJSONataTransformAction(),
createReplaceInFileAction()
]
const actions = [
...builtInActions,
...cnoeActions,
...roadieUtilActions
];
return await createRouter({
actions: actions,
logger: env.logger,
config: env.config,
database: env.database,
reader: env.reader,
catalogClient,
identity: env.identity,
permissions: env.permissions,
});
}
scaffolder.addActions(
createPublishGiteaAction({
integrations,
config,
}),
createArgoCDApp({
config,
logger,
}),
createKubernetesApply(config),
createSanitizeResource(),
createVerifyDependency(),
);
},
});
},
});

View file

@ -1,66 +0,0 @@
import { useHotCleanup } from '@backstage/backend-common';
import { createRouter } from '@backstage/plugin-search-backend';
import {
IndexBuilder,
LunrSearchEngine,
} from '@backstage/plugin-search-backend-node';
import { PluginEnvironment } from '../types';
import { DefaultCatalogCollatorFactory } from '@backstage/plugin-search-backend-module-catalog';
import { DefaultTechDocsCollatorFactory } from '@backstage/plugin-search-backend-module-techdocs';
import { Router } from 'express';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
// Initialize a connection to a search engine.
const searchEngine = new LunrSearchEngine({
logger: env.logger,
});
const indexBuilder = new IndexBuilder({
logger: env.logger,
searchEngine,
});
const schedule = env.scheduler.createScheduledTaskRunner({
frequency: { minutes: 10 },
timeout: { minutes: 15 },
// A 3 second delay gives the backend server a chance to initialize before
// any collators are executed, which may attempt requests against the API.
initialDelay: { seconds: 3 },
});
// Collators are responsible for gathering documents known to plugins. This
// collator gathers entities from the software catalog.
indexBuilder.addCollator({
schedule,
factory: DefaultCatalogCollatorFactory.fromConfig(env.config, {
discovery: env.discovery,
tokenManager: env.tokenManager,
}),
});
// collator gathers entities from techdocs.
indexBuilder.addCollator({
schedule,
factory: DefaultTechDocsCollatorFactory.fromConfig(env.config, {
discovery: env.discovery,
logger: env.logger,
tokenManager: env.tokenManager,
}),
});
// The scheduler controls when documents are gathered from collators and sent
// to the search engine for indexing.
const { scheduler } = await indexBuilder.build();
scheduler.start();
useHotCleanup(module, () => scheduler.stop());
return await createRouter({
engine: indexBuilder.getSearchEngine(),
types: indexBuilder.getDocumentTypes(),
permissions: env.permissions,
config: env.config,
logger: env.logger,
});
}

View file

@ -1,51 +0,0 @@
import { DockerContainerRunner } from '@backstage/backend-common';
import {
createRouter,
Generators,
Preparers,
Publisher,
} from '@backstage/plugin-techdocs-backend';
import Docker from 'dockerode';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
// Preparers are responsible for fetching source files for documentation.
const preparers = await Preparers.fromConfig(env.config, {
logger: env.logger,
reader: env.reader,
});
// Docker client (conditionally) used by the generators, based on techdocs.generators config.
const dockerClient = new Docker();
const containerRunner = new DockerContainerRunner({ dockerClient });
// Generators are used for generating documentation sites.
const generators = await Generators.fromConfig(env.config, {
logger: env.logger,
containerRunner,
});
// Publisher is used for
// 1. Publishing generated files to storage
// 2. Fetching files from storage and passing them to TechDocs frontend.
const publisher = await Publisher.fromConfig(env.config, {
logger: env.logger,
discovery: env.discovery,
});
// checks if the publisher is working and logs the result
await publisher.getReadiness();
return await createRouter({
preparers,
generators,
publisher,
logger: env.logger,
config: env.config,
discovery: env.discovery,
cache: env.cache,
});
}

View file

@ -1,6 +1,6 @@
import { executeShellCommand } from '@backstage/plugin-scaffolder-node';
import { createTemplateAction }from '@backstage/plugin-scaffolder-node';
import {Writable} from 'stream';
import { createTemplateAction } from '@backstage/plugin-scaffolder-node';
import { Writable } from 'stream';
class ConsoleLogStream extends Writable {
data: string;
@ -11,8 +11,8 @@ class ConsoleLogStream extends Writable {
}
_write(chunk: any, _: any, callback: any) {
this.data += chunk.toString(); // Convert the chunk to a string and append it to this.data
console.log(this.data)
this.data += chunk.toString(); // Convert the chunk to a string and append it to this.data
console.log(this.data);
callback();
}
}
@ -39,29 +39,31 @@ export const createVerifyDependency = () => {
},
},
async handler(ctx) {
const verifiers = ctx.input.verifiers
const verifiers = ctx.input.verifiers;
if (verifiers === null || verifiers.length === 0) {
ctx.logger.error('no verifier was supplied for the object')
return
ctx.logger.error('no verifier was supplied for the object');
return;
}
const baseCommand = 'cnoe'
const baseArguments = ['k8s', 'verify']
const baseCommand = 'cnoe';
const baseArguments = ['k8s', 'verify'];
verifiers.forEach((verifier: string) => baseArguments.push("--config", verifier))
verifiers.forEach((verifier: string) =>
baseArguments.push('--config', verifier),
);
const logStream = new ConsoleLogStream({});
await executeShellCommand({
command: baseCommand,
args: baseArguments,
logStream: logStream,
}).then(() =>
ctx.logger.info("verification succeeded")
).catch((error) => {
ctx.logger.error(error)
throw new Error(logStream.data)
});
})
.then(() => ctx.logger.info('verification succeeded'))
.catch(error => {
ctx.logger.error(error);
throw new Error(logStream.data);
});
},
});
};

View file

@ -24,9 +24,9 @@
"postpack": "backstage-cli package postpack"
},
"dependencies": {
"@backstage/core-components": "^0.13.8",
"@backstage/core-plugin-api": "^1.8.2",
"@backstage/theme": "^0.5.0",
"@backstage/core-components": "^0.14.6",
"@backstage/core-plugin-api": "^1.9.2",
"@backstage/theme": "^0.5.3",
"@material-ui/core": "^4.9.13",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "^4.0.0-alpha.61",
@ -36,10 +36,10 @@
"react": "^16.13.1 || ^17.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.25.1",
"@backstage/core-app-api": "^1.11.3",
"@backstage/dev-utils": "^1.0.26",
"@backstage/test-utils": "^1.4.7",
"@backstage/cli": "^0.26.4",
"@backstage/core-app-api": "^1.12.4",
"@backstage/dev-utils": "^1.0.31",
"@backstage/test-utils": "^1.5.4",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^12.1.3",
"@testing-library/user-event": "^14.0.0",

View file

@ -24,9 +24,9 @@
"postpack": "backstage-cli package postpack"
},
"dependencies": {
"@backstage/core-components": "^0.13.8",
"@backstage/core-plugin-api": "^1.8.2",
"@backstage/theme": "^0.5.0",
"@backstage/core-components": "^0.14.6",
"@backstage/core-plugin-api": "^1.9.2",
"@backstage/theme": "^0.5.3",
"@material-ui/core": "^4.9.13",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "^4.0.0-alpha.61",
@ -36,10 +36,10 @@
"react": "^16.13.1 || ^17.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.25.1",
"@backstage/core-app-api": "^1.11.3",
"@backstage/dev-utils": "^1.0.26",
"@backstage/test-utils": "^1.4.7",
"@backstage/cli": "^0.26.4",
"@backstage/core-app-api": "^1.12.4",
"@backstage/dev-utils": "^1.0.31",
"@backstage/test-utils": "^1.5.4",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^12.1.3",
"@testing-library/user-event": "^14.0.0",

View file

@ -24,9 +24,9 @@
"postpack": "backstage-cli package postpack"
},
"dependencies": {
"@backstage/core-components": "~0.13.10",
"@backstage/core-plugin-api": "~1.8.2",
"@backstage/theme": "~0.5.0",
"@backstage/core-components": "^0.14.6",
"@backstage/core-plugin-api": "^1.9.2",
"@backstage/theme": "^0.5.3",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "^4.0.0-alpha.61",
@ -36,10 +36,10 @@
"react": "^17.0.0 || ^18.0.2"
},
"devDependencies": {
"@backstage/cli": "^0.25.1",
"@backstage/core-app-api": "^1.11.3",
"@backstage/dev-utils": "^1.0.26",
"@backstage/test-utils": "^1.4.7",
"@backstage/cli": "^0.26.4",
"@backstage/core-app-api": "^1.12.4",
"@backstage/dev-utils": "^1.0.31",
"@backstage/test-utils": "^1.5.4",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^12.1.3",
"@testing-library/user-event": "^14.0.0",

5921
yarn.lock

File diff suppressed because it is too large Load diff