From e265f47af110a436ce623039ca033f1526855d2d Mon Sep 17 00:00:00 2001 From: Sahil Budhwar Date: Sat, 28 Dec 2024 19:39:04 +0530 Subject: [PATCH] fix: add scripts to create N number of components --- .eslintignore | 1 + package.json | 4 +- scripts/components.mjs | 90 ++++++++++++++++++++++++++++++++++++++++++ scripts/utils.mjs | 65 ++++++++++++++++++++++++++++++ tsconfig.json | 2 +- yarn.lock | 35 ++++++++++++++++ 6 files changed, 195 insertions(+), 2 deletions(-) create mode 100755 scripts/components.mjs create mode 100644 scripts/utils.mjs diff --git a/.eslintignore b/.eslintignore index 2c62716b..f703d093 100644 --- a/.eslintignore +++ b/.eslintignore @@ -7,3 +7,4 @@ e2e-tests/* commitlint.config.js **.config.js config/* +scripts/* diff --git a/package.json b/package.json index cd267a30..952b3e4e 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "lint": "yarn lint:ts && yarn lint:sass", "lint:ts": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "lint:sass": "stylelint 'src/**/*.scss' --config .stylelintrc.json", + "script": "zx ./scripts/components.mjs", "prepare": "husky" }, "dependencies": { @@ -98,7 +99,8 @@ "webpack-cli": "^5.1.4", "webpack-dev-server": "^5.0.4", "webpack-merge": "^6.0.1", - "whatwg-fetch": "^3.6.20" + "whatwg-fetch": "^3.6.20", + "zx": "^8.3.0" }, "sideEffects": [ "*.css", diff --git a/scripts/components.mjs b/scripts/components.mjs new file mode 100755 index 00000000..413a4e4e --- /dev/null +++ b/scripts/components.mjs @@ -0,0 +1,90 @@ +#!/usr/bin/env zx + +import { + checkSafetyThresholds, + createResource, + getCurrentNamespace, + promptForNamespace, +} from './utils.mjs'; + +const NumberOfComponent = 2; + +const baseApplicationConfig = { + apiVersion: 'appstudio.redhat.com/v1alpha1', + kind: 'Application', + metadata: { + name: 'test-application-n-components', + namespace: 'NAMESPACE', + annotations: { 'application.thumbnail': '9' }, + }, + spec: { displayName: 'Testing Application (100 components)' }, +}; + +const baseComponentConfig = { + apiVersion: 'appstudio.redhat.com/v1alpha1', + kind: 'Component', + metadata: { + annotations: { + 'build.appstudio.openshift.io/pipeline': '{"name":"docker-build","bundle":"latest"}', + 'build.appstudio.openshift.io/request': 'configure-pac', + 'image.redhat.com/generate': '{"visibility": "public"}', + }, + name: 'COMPONENT_METADATA_NAME_PLACEHOLDER', + }, + spec: { + componentName: 'COMPONENT_NAME_PLACEHOLDER', + application: 'test-application-n-components', + source: { + git: { + url: 'https://github.com/sahil143/devfile-sample-code-with-quarkus', + }, + }, + }, +}; + +const allConfigs = []; + +// Safety check +const shouldProceed = await checkSafetyThresholds(NumberOfComponent); +if (!shouldProceed) { + console.log(chalk.blue('Operation cancelled by user')); + process.exit(0); +} + +// Get and confirm namespace +const currentNs = await getCurrentNamespace(); +const targetNs = await promptForNamespace(currentNs); + +console.log(chalk.green('Using namespace', targetNs)); + +// Create application +console.log( + chalk.grey( + `Creating application '${baseApplicationConfig.spec.displayName}'(${baseApplicationConfig.metadata.name})`, + allConfigs, + ), +); +baseApplicationConfig.metadata['namespace'] = targetNs; + +await createResource(baseApplicationConfig, 'application'); + +console.log(chalk.grey(`Creating ${NumberOfComponent} component`, allConfigs)); + +for (let i = 1; i <= NumberOfComponent; i++) { + // Create a deep copy of the base config + const config = JSON.parse(JSON.stringify(baseComponentConfig)); + + // Update with unique names + config.metadata.name = `devfile-sample-code-with-quarkus-longer-name-new-${i}`; + config.metadata['namespace'] = targetNs; + config.spec.componentName = `devfile-sample-code-with-quarkus-longer-name-new-${i}`; + + allConfigs.push(config); +} + +// Apply each config using kubectl with JSON +for (const config of allConfigs) { + await createResource(config, 'component'); +} + +console.log(chalk.blue(`✨ Successfully processed ${allConfigs.length} components`)); diff --git a/scripts/utils.mjs b/scripts/utils.mjs new file mode 100644 index 00000000..42997ad2 --- /dev/null +++ b/scripts/utils.mjs @@ -0,0 +1,65 @@ +const DELAY = 10000; + +// Function to get current namespace +export async function getCurrentNamespace() { + try { + const result = await $`kubectl config view --minify -o jsonpath='{..namespace}'`; + return result.stdout.trim() || 'default'; + } catch (error) { + return 'default'; + } +} + +// Function to prompt for namespace +export async function promptForNamespace(currentNs) { + console.log(chalk.blue(`Current namespace is: ${currentNs}`)); + const useCurrentNs = await question( + chalk.yellow(`Do you want to use the current namespace "${currentNs}"? (y/n) `), + ); + + if (useCurrentNs.toLowerCase() === 'y') { + return currentNs; + } + + const newNs = await question(chalk.yellow('Enter the namespace to use: ')); + return newNs.trim(); +} + +// sleep function +export async function sleep(time) { + return await new Promise((resolve) => setTimeout(resolve, time)); +} + +export async function createResource(config, type = 'resource') { + try { + await $`kubectl apply --validate=false -f - <<< ${JSON.stringify(config)}`; + console.log(chalk.green(`✓ Created ${type}: ${config.metadata.name}`)); + // Sleep for 10 second. Disclaimer: Do not create too many component wi + await sleep(DELAY); + } catch (error) { + console.log(chalk.red(`✗ Failed to create ${type} ${config.metadata.name}: ${error.message}`)); + process.exit(1); + } +} + +export async function checkSafetyThresholds(numberOfComponents) { + if (numberOfComponents > 10) { + console.log(chalk.yellow('\n CAUTION:')); + console.log(chalk.yellow(`You are about to create ${numberOfComponents} components.`)); + console.log(chalk.yellow('Creating too many components in quick succession might:')); + console.log(chalk.yellow(' - Overload the API server')); + console.log(chalk.yellow(' - Trigger rate limiting')); + console.log(chalk.yellow(' - Cause failed deployments')); + console.log(chalk.yellow(`\nCurrent delay between components: ${DELAY / 1000} seconds`)); + + if (DELAY < 10000) { + console.log( + chalk.yellow(`\nRecommended: Use a delay of at least 10 seconds between components`), + ); + } + + const proceed = await question(chalk.yellow('\nDo you want to proceed? (y/N) ')); + return proceed.toLowerCase() === 'y'; + } + return true; +} diff --git a/tsconfig.json b/tsconfig.json index b5396b28..1992a0da 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,5 +22,5 @@ "noFallthroughCasesInSwitch": true }, "include": ["src/", "@types"], - "exclude": ["*.config.js", ".*.cjs", "node_modules", "config/*"] + "exclude": ["*.config.js", ".*.cjs", "node_modules", "config/*", "scripts/*"] } diff --git a/yarn.lock b/yarn.lock index 4de1106b..66c95eeb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1576,6 +1576,14 @@ "@types/qs" "*" "@types/serve-static" "*" +"@types/fs-extra@>=11": + version "11.0.4" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-11.0.4.tgz#e16a863bb8843fba8c5004362b5a73e17becca45" + integrity sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ== + dependencies: + "@types/jsonfile" "*" + "@types/node" "*" + "@types/geojson@*": version "7946.0.14" resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.14.tgz#319b63ad6df705ee2a65a73ef042c8271e696613" @@ -1659,6 +1667,13 @@ resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== +"@types/jsonfile@*": + version "6.1.4" + resolved "https://registry.yarnpkg.com/@types/jsonfile/-/jsonfile-6.1.4.tgz#614afec1a1164e7d670b4a7ad64df3e7beb7b702" + integrity sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ== + dependencies: + "@types/node" "*" + "@types/lodash-es@^4.17.12": version "4.17.12" resolved "https://registry.yarnpkg.com/@types/lodash-es/-/lodash-es-4.17.12.tgz#65f6d1e5f80539aa7cfbfc962de5def0cf4f341b" @@ -1690,6 +1705,13 @@ dependencies: undici-types "~5.26.4" +"@types/node@>=20": + version "22.10.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.10.2.tgz#a485426e6d1fdafc7b0d4c7b24e2c78182ddabb9" + integrity sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ== + dependencies: + undici-types "~6.20.0" + "@types/prop-types@*": version "15.7.12" resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz" @@ -8407,6 +8429,11 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== +undici-types@~6.20.0: + version "6.20.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" + integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== + unicorn-magic@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.1.0.tgz#1bb9a51c823aaf9d73a8bfcd3d1a23dde94b0ce4" @@ -8924,3 +8951,11 @@ yup@^0.32.11: nanoclone "^0.2.1" property-expr "^2.0.4" toposort "^2.0.2" + +zx@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/zx/-/zx-8.3.0.tgz#5a4750662041c5990fad1abd02743208c6555b6b" + integrity sha512-L8mY3yfJwo3a8ZDD6f9jZzAcRWJZYcV8GauZmBxLB/aSTwaMzMIEVpPp2Kyx+7yF0gdvuxKnMxAZRft9UCawiw== + optionalDependencies: + "@types/fs-extra" ">=11" + "@types/node" ">=20"