diff --git a/sreez-showcase/app-config.yaml b/sreez-showcase/app-config.yaml index 133e6f7..656ed06 100644 --- a/sreez-showcase/app-config.yaml +++ b/sreez-showcase/app-config.yaml @@ -48,7 +48,7 @@ proxy: # Set this variable by using this command # export ARGOCD_AUTH_TOKEN="argocd.token=$(argocd --server argocd-server-sreez.apps.oc-med.wk.nt.local:443 --insecure account generate-token --account backstage)" # Use the appropriate account as configured in your argo-cm deployment ConfigMap - $env: ARGOCD_AUTH_TOKEN + $env: BACKSTAGE_ARGOCD_AUTH_TOKEN ### Example for how to add a proxy endpoint for the frontend. ### A typical reason to do this is to handle HTTPS and CORS for internal services. # endpoints: diff --git a/sreez-showcase/packages/backend/src/index.ts b/sreez-showcase/packages/backend/src/index.ts index 5d3ab66..5af52af 100644 --- a/sreez-showcase/packages/backend/src/index.ts +++ b/sreez-showcase/packages/backend/src/index.ts @@ -49,4 +49,30 @@ backend.add(import('@backstage/plugin-search-backend/alpha')); backend.add(import('@backstage/plugin-devtools-backend')); +import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; +import { createBackendModule } from '@backstage/backend-plugin-api'; +import { createNewFileAction } from './plugins/scaffolder/actions/custom'; +import { createArgoProjectAction } from './plugins/scaffolder/actions/argo'; + +const scaffolderModuleCustomExtensions = createBackendModule({ + pluginId: 'scaffolder', // name of the plugin that the module is targeting + moduleId: 'custom-extensions', + register(env) { + env.registerInit({ + deps: { + scaffolder: scaffolderActionsExtensionPoint, + // ... and other dependencies as needed + }, + async init({ scaffolder /* ..., other dependencies */ }) { + // Here you have the opportunity to interact with the extension + // point before the plugin itself gets instantiated + scaffolder.addActions(createNewFileAction()); // just an example + scaffolder.addActions(createArgoProjectAction()); // just an example + }, + }); + }, +}); + +backend.add(scaffolderModuleCustomExtensions()); + backend.start(); diff --git a/sreez-showcase/packages/backend/src/plugins/scaffolder/actions/argo.ts b/sreez-showcase/packages/backend/src/plugins/scaffolder/actions/argo.ts new file mode 100644 index 0000000..0cc32b2 --- /dev/null +++ b/sreez-showcase/packages/backend/src/plugins/scaffolder/actions/argo.ts @@ -0,0 +1,69 @@ +import { exec } from 'child_process'; +import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; +import { z } from 'zod'; + +export const createArgoProjectAction = () => { + return createTemplateAction({ + id: 'argocd:app:create', + schema: { + input: z.object({ + appName: z.string().describe('The name of the application'), + argoInstance: z.string().describe('The ARGO instance URL'), + namespace: z.string().describe('The Kubernetes namespace'), + repoUrl: z.string().describe('The repository URL to publish the artifacts'), + repoPath: z.string().describe('The path in the repository that argo must watch'), + labelValue: z.string().describe('The value for the label based on the app name'), + valuesFiles: z.array(z.string()).describe('List of value files'), + }), + }, + + async handler(ctx) { + createArgoProject(ctx.input) + // throw new Error(`Yay we are in handler with ctx ${ctx}`) + }, + }); +}; + +type ArgoProjectParams = { + appName: string; + argoInstance: string; + namespace: string; + repoUrl: string; + repoPath: string; + labelValue: string; + valuesFiles: string[]; +} + +function createArgoProject(params: ArgoProjectParams): void { + const command = `argocd app create ${params.appName} --project default --repo ${params.repoUrl} --path ${params.repoPath} --dest-server https://kubernetes.default.svc --dest-namespace ${params.namespace} --sync-policy auto`; + + exec(command, (error, stdout, stderr) => { + if (error) { + console.error(`exec error: ${error}`); + throw new Error(`createArgoProject failed due to ${error}`); + } + + console.log(`stdout: ${stdout}`); + console.error(`stderr: ${stderr}`); + }); + + executeWithRetry(command, 1, 5); // Start the retry mechanism with the first attempt +} + + +function executeWithRetry(command: string, attempt: number, maxAttemps: number = 5): void { + exec(command, (error, stdout, stderr) => { + if (error) { + console.error(`exec error on attempt ${attempt}: ${error}`); + + // If it's not the last retry, wait for 2 seconds and try again + if (attempt < maxAttemps) { + setTimeout(() => executeWithRetry(command, attempt + 1, maxAttemps), 2000); + } else { + console.error(`Failed after ${attempt} attempts: ${stderr}`); + } + } else { + console.log(`stdout: ${stdout}`); + } + }); +} diff --git a/sreez-showcase/packages/backend/src/plugins/scaffolder/actions/custom.ts b/sreez-showcase/packages/backend/src/plugins/scaffolder/actions/custom.ts new file mode 100644 index 0000000..db412e7 --- /dev/null +++ b/sreez-showcase/packages/backend/src/plugins/scaffolder/actions/custom.ts @@ -0,0 +1,21 @@ +import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; +import { z } from 'zod'; + +export const createNewFileAction = () => { + return createTemplateAction({ + id: 'acme:file:create', + schema: { + input: z.object({ + contents: z.string().describe('The contents of the file'), + filename: z + .string() + .describe('The filename of the file that will be created'), + }), + }, + + async handler(ctx) { + throw new Error(`Yay we are in handler with ctx ${ctx}`) + }, + }); +}; + diff --git a/sreez-showcase/yarn.lock b/sreez-showcase/yarn.lock index 2dad1f0..183ff8c 100644 --- a/sreez-showcase/yarn.lock +++ b/sreez-showcase/yarn.lock @@ -9287,9 +9287,9 @@ integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== "@types/react-dom@*", "@types/react-dom@^18", "@types/react-dom@^18.0.0": - version "18.3.0" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.0.tgz#0cbc818755d87066ab6ca74fbedb2547d74a82b0" - integrity sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg== + version "18.2.25" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.25.tgz#2946a30081f53e7c8d585eb138277245caedc521" + integrity sha512-o/V48vf4MQh7juIKZU2QGDfli6p1+OOi5oXx36Hffpc9adsHeXjVp8rHuPkjd8VT8sOJ2Zp05HR7CdpGTIUFUA== dependencies: "@types/react" "*" @@ -9325,9 +9325,9 @@ "@types/react" "*" "@types/react@*", "@types/react@^16.13.1 || ^17.0.0", "@types/react@^16.13.1 || ^17.0.0 || ^18.0.0", "@types/react@^18": - version "18.3.1" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.1.tgz#fed43985caa834a2084d002e4771e15dfcbdbe8e" - integrity sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw== + version "18.2.79" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.79.tgz#c40efb4f255711f554d47b449f796d1c7756d865" + integrity sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w== dependencies: "@types/prop-types" "*" csstype "^3.0.2" @@ -23238,7 +23238,16 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -23316,7 +23325,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@6.0, strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -23330,6 +23339,13 @@ strip-ansi@5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@6.0, strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -25183,7 +25199,7 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -25201,6 +25217,15 @@ wrap-ansi@^6.0.1: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"