From 862b167da602b529d74daafca05615fb9497259e Mon Sep 17 00:00:00 2001 From: Alberto Martino Date: Fri, 13 Jun 2025 23:02:16 +0200 Subject: [PATCH 01/66] Fix typos in log messages and file paths within deploy scripts --- ide/deploy/deploy.js | 2 +- ide/deploy/scan.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ide/deploy/deploy.js b/ide/deploy/deploy.js index fc4e7978..4b3184a2 100644 --- a/ide/deploy/deploy.js +++ b/ide/deploy/deploy.js @@ -163,7 +163,7 @@ export function deployProject(artifact) { if (manifestContent.indexOf('packages:')!==-1) { exec(`$OPS -wsk project deploy --manifest ${artifact}`); } else { - console.log(`Wanring: it seems that the ${artifact} file is not a valid manifest file. Skipping`); + console.log(`Warning: it seems that the ${artifact} file is not a valid manifest file. Skipping`); } } } diff --git a/ide/deploy/scan.js b/ide/deploy/scan.js index 3fdce76b..67f1c28f 100644 --- a/ide/deploy/scan.js +++ b/ide/deploy/scan.js @@ -158,9 +158,9 @@ export async function scan() { manifests.sort((a, b) => a.localeCompare(b)); } if (manifests.length >0 ) { - if (Bun.file('packeges/.env')) { + if (Bun.file('packages/.env')) { console.log("Found packages .env file. Reading it"); - config({ path: "./package/.env" }); + config({ path: "./packages/.env" }); } for (const manifest of manifests) { console.log(">>> Manifest:", manifest); From 95df455844428072c7a1d83a0e71b60f15ef7da7 Mon Sep 17 00:00:00 2001 From: Alberto Martino Date: Mon, 16 Jun 2025 17:27:29 +0200 Subject: [PATCH 02/66] Add utilities for managing deployment info: sync, remove, and cleanup --- ide/deploy/syncDeployInfo.js | 107 +++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 ide/deploy/syncDeployInfo.js diff --git a/ide/deploy/syncDeployInfo.js b/ide/deploy/syncDeployInfo.js new file mode 100644 index 00000000..15e02ea2 --- /dev/null +++ b/ide/deploy/syncDeployInfo.js @@ -0,0 +1,107 @@ +import {existsSync, mkdirSync, writeFileSync, readFileSync} from "fs"; + +/** + * Synchronizes deployment information by saving the provided package and deployment data + * to a persisted `.ops/deployment.json` file. If the directory `.ops` does not exist, it + * is created before saving the information. + * + * The deployment information is organized by packages, with each package containing an array + * of its actions. This allows for more granular control when undeploying specific actions. + * + * @param {Set} packages - A set of package names to include in the deployment data. + * @param {Set} deployments - A set of deployment identifiers to include in the deployment data. + * @return {void} + */ +export function syncDeployInfo(packages, deployments) { + if (!existsSync('.ops')) { + mkdirSync('.ops', { recursive: true }); + } + + // Create a structured object with packages as keys and arrays of actions as values + const packageActions = {}; + + // Initialize packages with empty arrays + for (const pkg of packages) { + packageActions[pkg] = []; + } + + // Add actions to their respective packages + for (const deployment of deployments) { + try { + const sp = deployment.split("/"); + const spData = sp[sp.length - 1].split("."); + const name = spData[0]; + const pkg = sp[1]; + + // If the package exists in our structure, add the action to it + if (packageActions[pkg]) { + packageActions[pkg].push(name); + } + } catch (error) { + console.error(`Error parsing deployment path ${deployment}:`, error); + } + } + + const deploymentInfo = { + packages: Array.from(packages), + packageActions: packageActions + }; + + writeFileSync('.ops/deployment.json', JSON.stringify(deploymentInfo, null, 2)); + console.log("> Saved deployment information to .ops/deployment.json"); +} + +/** + * Removes a specific action from the deployment information. + * + * @param {string} actionName - The name of the action to remove in the format "package/action". + * @return {boolean} - True if the action was found and removed, false otherwise. + */ +export function removeActionFromDeployInfo(actionName) { + if (!existsSync('.ops/deployment.json')) { + console.error('Error: No deployment information found.'); + return false; + } + + try { + const deploymentInfo = JSON.parse(readFileSync('.ops/deployment.json', 'utf8')); + const [pkg, action] = actionName.split('/'); + + if (!deploymentInfo.packageActions || !deploymentInfo.packageActions[pkg]) { + console.error(`Error: Package ${pkg} not found in deployment information.`); + return false; + } + + const actionIndex = deploymentInfo.packageActions[pkg].indexOf(action); + if (actionIndex === -1) { + console.error(`Error: Action ${action} not found in package ${pkg}.`); + return false; + } + + // Remove the action from the package + deploymentInfo.packageActions[pkg].splice(actionIndex, 1); + + // If the package has no more actions, remove it from the packages list + if (deploymentInfo.packageActions[pkg].length === 0) { + const packageIndex = deploymentInfo.packages.indexOf(pkg); + if (packageIndex !== -1) { + deploymentInfo.packages.splice(packageIndex, 1); + } + delete deploymentInfo.packageActions[pkg]; + } + + writeFileSync('.ops/deployment.json', JSON.stringify(deploymentInfo, null, 2)); + console.log(`> Removed ${actionName} from deployment information.`); + return true; + } catch (error) { + console.error("Error updating deployment information:", error); + return false; + } +} + +/** + * Cleans up deployment information by resetting and synchronizing deployment data. + */ +export function cleanupDeployInfo() { + syncDeployInfo(new Set(), new Set()); +} From fb5a109d05e2d775df7cc075579f0eebc1f5af0f Mon Sep 17 00:00:00 2001 From: Alberto Martino Date: Mon, 16 Jun 2025 17:27:53 +0200 Subject: [PATCH 03/66] sync deploy info when executing deploy command --- ide/deploy/scan.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ide/deploy/scan.js b/ide/deploy/scan.js index 67f1c28f..8143486b 100644 --- a/ide/deploy/scan.js +++ b/ide/deploy/scan.js @@ -19,6 +19,7 @@ import {glob} from 'glob'; import {buildAction, buildZip, deployAction, deployPackage, deployProject} from './deploy.js'; import {getOpenServerlessConfig} from './client.js'; import {config} from "dotenv"; +import {syncDeployInfo} from "./syncDeployInfo"; /** * This function will prepare and deploy the functions in `packages` directory. @@ -167,4 +168,11 @@ export async function scan() { deployProject(manifest); } } + + try { + // Save deployment information with the new structure + syncDeployInfo(packages, deployments); + } catch (error) { + console.error("Error saving deployment information:", error); + } } From 2f521257f30f620c75d17c69bf4aaa701f4968c9 Mon Sep 17 00:00:00 2001 From: Alberto Martino Date: Mon, 16 Jun 2025 17:28:23 +0200 Subject: [PATCH 04/66] refactor undeploy task --- ide/opsfile.yml | 45 +++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/ide/opsfile.yml b/ide/opsfile.yml index e2f8a9e4..3bf3ff6d 100644 --- a/ide/opsfile.yml +++ b/ide/opsfile.yml @@ -62,7 +62,7 @@ tasks: if test -e $PIDFILE then PID=$(cat $PIDFILE) - + if [ ! -z "$PID" ]; then if ps -p "$PID" > /dev/null; then @@ -139,8 +139,8 @@ tasks: else false fi - - + + poll: silent: true @@ -189,35 +189,32 @@ tasks: undeploy: - desc: undeploy all the actions - prompt: "are you sure you want to remove all actions and packages?" + desc: undeploy actions and packages from the current project silent: true cmds: - task: prereq - - > - $OPS action list - | awk 'NR>1 { print $1}' - | while read action ; - do if {{.__dry_run}} - then echo '$' $OPS action delete "$action" - else $OPS action delete "$action" - fi - done - - > - $OPS package list - | awk 'NR>1 { print $1}' - | while read package ; - do if {{.__dry_run}} - then echo '$' $OPS package delete "$package" - else $OPS package delete "$package" - fi - done + - | + if {{.__dry_run}} + then DRY="--dry-run" + else DRY="" + fi + + # Check if an action argument is provided + if test -n "{{._action_}}" + then + # Undeploy a specific action + echo "Undeploying specific action: {{._action_}}" + bun {{.TASKFILE_DIR}}/deploy/index.js "$OPS_PWD" -u -s "{{._action_}}" $DRY + else + # Undeploy all actions and packages from the current project + bun {{.TASKFILE_DIR}}/deploy/index.js "$OPS_PWD" -u $DRY + fi - > if {{.__dry_run}} then echo '$' $OPS util clean else $OPS util clean fi - + clean: From ad38a02b07da8e097870bf35ccd387a6d5555115 Mon Sep 17 00:00:00 2001 From: Alberto Martino Date: Mon, 16 Jun 2025 17:28:33 +0200 Subject: [PATCH 05/66] add undeploy functionality to deployment script also update docopts --- ide/deploy/index.js | 36 ++++++++++++++++++++++++++++++++++-- ide/docopts.md | 4 ++-- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/ide/deploy/index.js b/ide/deploy/index.js index eb96f72e..93b804ce 100644 --- a/ide/deploy/index.js +++ b/ide/deploy/index.js @@ -22,6 +22,7 @@ import {program} from 'commander'; import {scan} from './scan.js'; import {watchAndDeploy} from './watch.js'; import {setDryRun, deploy} from './deploy.js'; +import {undeploy, setDryRun as setUndeployDryRun} from './undeploy.js'; import {build} from './client.js'; function signalHandler() { @@ -65,15 +66,46 @@ async function main() { .option('-d, --deploy', 'Deploy') .option('-w, --watch', 'Watch for changes') .option('-s, --single ', 'Deploy a single action, either a single file or a directory.', '') + .option('-u, --undeploy', 'Undeploy actions and packages from the current project') .parse(process.argv); const options = program.opts(); const directory = program.args[0]; setDryRun(options.dryRun); + setUndeployDryRun(options.dryRun); process.chdir(directory); - if (options.watch) { + if (options.undeploy) { + // Undeploy actions and packages from the current project + let success; + + if (options.single !== '') { + // If a single action is specified, undeploy just that action + let action = options.single; + if (!action.startsWith('packages/')) { + action = `packages/${action}`; + } + + // Extract the package and action name + const parts = action.split('/'); + if (parts.length >= 3) { + const pkg = parts[1]; + const actionName = parts[2].split('.')[0]; // Remove file extension if present + success = undeploy(`${pkg}/${actionName}`); + } else { + // If the format is already package/action + success = undeploy(action); + } + } else { + // Otherwise, undeploy all actions and packages from the current project + success = undeploy(); + } + + if (!success) { + process.exit(1); + } + } else if (options.watch) { checkPort(); if (!options.fast) { await scan(); @@ -102,4 +134,4 @@ async function main() { main().catch(err => { console.error(err); process.exit(1); -}); \ No newline at end of file +}); diff --git a/ide/docopts.md b/ide/docopts.md index aeb98594..b47d0cfc 100644 --- a/ide/docopts.md +++ b/ide/docopts.md @@ -44,7 +44,7 @@ Usage: ide login login in openserverless ide devel activate development mode ide deploy deploy everything or just one action - ide undeploy undeploy everything or just one action + ide undeploy undeploy actions and packages from the current project or just one action ide clean clean the temporay files ide setup setup the ide ide serve serve web area @@ -60,4 +60,4 @@ Usage: ``` --fast Skip the initial deployment step and go in incremental update mode --dry-run Simulates the execution without making any actual changes -``` \ No newline at end of file +``` From 7a3ce0df7b0dad461283176a90359d3e2c888d12 Mon Sep 17 00:00:00 2001 From: Alberto Martino Date: Mon, 16 Jun 2025 17:29:05 +0200 Subject: [PATCH 06/66] add undeploy functionality for actions and packages to deployment script --- ide/deploy/undeploy.js | 115 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 ide/deploy/undeploy.js diff --git a/ide/deploy/undeploy.js b/ide/deploy/undeploy.js new file mode 100644 index 00000000..50ced80f --- /dev/null +++ b/ide/deploy/undeploy.js @@ -0,0 +1,115 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import { existsSync, readFileSync } from 'fs'; +import { spawnSync } from 'child_process'; +import { cleanupDeployInfo, removeActionFromDeployInfo } from "./syncDeployInfo"; + +let dryRun = false; + +export function setDryRun(b) { + dryRun = b; +} + +function exec(cmd) { + console.log("$", cmd); + if (!dryRun) { + spawnSync(cmd, { shell: true, env: process.env, stdio: "inherit" }); + } +} + +/** + * Undeploy a specific action and update the deployment information + * @param {string} actionName - The name of the action to undeploy in the format "package/action" + * @returns {boolean} true if successful, false if error + */ +export function undeployAction(actionName) { + console.log(`> Undeploying action: ${actionName}`); + + try { + // Execute the undeploy command + exec(`$OPS action delete ${actionName}`); + + // Update the deployment information + const success = removeActionFromDeployInfo(actionName); + if (success) { + console.log(`> Action ${actionName} successfully undeployed and removed from deployment information.`); + return true; + } else { + console.error(`> Action ${actionName} was undeployed but could not be removed from deployment information.`); + return false; + } + } catch (error) { + console.error(`Error undeploying action ${actionName}:`, error); + return false; + } +} + +/** + * Undeploy actions and packages based on the deployment information in .ops/deployment.json + * If no deployment information is found, return an error + * @param {string} [specificAction] - Optional specific action to undeploy + * @returns {boolean} true if successful, false if error + */ +export function undeploy(specificAction) { + // If a specific action is provided, undeploy just that action + if (specificAction) { + return undeployAction(specificAction); + } + + // Otherwise, undeploy all actions and packages from the current project + if (!existsSync('.ops/deployment.json')) { + console.error('Error: No OpenServerless project found in the current directory.'); + console.error('Please run this command in a directory with an OpenServerless project.'); + return false; + } + + try { + const deploymentInfo = JSON.parse(readFileSync('.ops/deployment.json', 'utf8')); + const { packages, packageActions } = deploymentInfo; + + if (!packages || !packageActions || packages.length === 0) { + console.error('Error: No deployment information found.'); + return false; + } + + console.log("> Undeploy actions and packages from the current project:"); + + // Undeploy actions + for (const pkg of packages) { + const actions = packageActions[pkg] || []; + for (const action of actions) { + const actionName = `${pkg}/${action}`; + console.log(`>> Undeploy action: ${actionName}`); + exec(`$OPS action delete ${actionName}`); + } + } + + // Undeploy packages + for (const pkg of packages) { + console.log(`>> Undeploy package: ${pkg}`); + exec(`$OPS package delete ${pkg}`); + } + + cleanupDeployInfo(); + console.log("> Undeployment completed successfully."); + return true; + } catch (error) { + console.error("Error undeploy:", error); + return false; + } +} From 6e75b0783e4f0f8d1408caac4e99a31c22fe0fdf Mon Sep 17 00:00:00 2001 From: Bruno Salzano Date: Sat, 12 Jul 2025 21:23:40 +0200 Subject: [PATCH 07/66] fix: minor modifications added a process.exit inside undeploy. Added missing license header on info.js and syncDeployInfo.js. Added icons on error messages --- ide/deploy/index.js | 1 + ide/deploy/info.js | 17 +++++++++++++++++ ide/deploy/syncDeployInfo.js | 23 ++++++++++++++++++++--- ide/deploy/undeploy.js | 8 ++++---- 4 files changed, 42 insertions(+), 7 deletions(-) diff --git a/ide/deploy/index.js b/ide/deploy/index.js index 8c884b4f..0334c0b8 100644 --- a/ide/deploy/index.js +++ b/ide/deploy/index.js @@ -114,6 +114,7 @@ async function main() { if (!success) { process.exit(1); } + process.exit(0); } else if (options.watch) { checkPort(); if (!options.fast) { diff --git a/ide/deploy/info.js b/ide/deploy/info.js index 1f67de48..2d739e3f 100644 --- a/ide/deploy/info.js +++ b/ide/deploy/info.js @@ -1,3 +1,20 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + import { getOpenServerlessConfig} from './client.js'; import {program} from "commander"; import process from "process"; diff --git a/ide/deploy/syncDeployInfo.js b/ide/deploy/syncDeployInfo.js index 15e02ea2..6865019b 100644 --- a/ide/deploy/syncDeployInfo.js +++ b/ide/deploy/syncDeployInfo.js @@ -1,3 +1,20 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + import {existsSync, mkdirSync, writeFileSync, readFileSync} from "fs"; /** @@ -68,13 +85,13 @@ export function removeActionFromDeployInfo(actionName) { const [pkg, action] = actionName.split('/'); if (!deploymentInfo.packageActions || !deploymentInfo.packageActions[pkg]) { - console.error(`Error: Package ${pkg} not found in deployment information.`); + console.error(`❌ Error: Package ${pkg} not found in deployment information.`); return false; } const actionIndex = deploymentInfo.packageActions[pkg].indexOf(action); if (actionIndex === -1) { - console.error(`Error: Action ${action} not found in package ${pkg}.`); + console.error(`❌ Error: Action ${action} not found in package ${pkg}.`); return false; } @@ -94,7 +111,7 @@ export function removeActionFromDeployInfo(actionName) { console.log(`> Removed ${actionName} from deployment information.`); return true; } catch (error) { - console.error("Error updating deployment information:", error); + console.error("❌ Error updating deployment information:", error); return false; } } diff --git a/ide/deploy/undeploy.js b/ide/deploy/undeploy.js index 50ced80f..2d252a11 100644 --- a/ide/deploy/undeploy.js +++ b/ide/deploy/undeploy.js @@ -73,8 +73,8 @@ export function undeploy(specificAction) { // Otherwise, undeploy all actions and packages from the current project if (!existsSync('.ops/deployment.json')) { - console.error('Error: No OpenServerless project found in the current directory.'); - console.error('Please run this command in a directory with an OpenServerless project.'); + console.error('❌ Error: No OpenServerless project found in the current directory.'); + console.error('❌ Please run this command in a directory with an OpenServerless project.'); return false; } @@ -83,7 +83,7 @@ export function undeploy(specificAction) { const { packages, packageActions } = deploymentInfo; if (!packages || !packageActions || packages.length === 0) { - console.error('Error: No deployment information found.'); + console.error('❌ Error: No deployment information found.'); return false; } @@ -109,7 +109,7 @@ export function undeploy(specificAction) { console.log("> Undeployment completed successfully."); return true; } catch (error) { - console.error("Error undeploy:", error); + console.error("❌ Error undeploy:", error); return false; } } From 837673b808c74ae6c7c8c7d3d4664014c6ec3d80 Mon Sep 17 00:00:00 2001 From: Bruno Salzano Date: Sat, 20 Sep 2025 08:51:14 +0200 Subject: [PATCH 08/66] feat: ingressclass config Added ingressclass config as specified in issue #173 --- config/docopts.md | 3 +++ config/opsfile.yml | 14 ++++++++++++-- setup/kubernetes/whisk.yaml | 1 + 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/config/docopts.md b/config/docopts.md index a41be77e..2549b82f 100644 --- a/config/docopts.md +++ b/config/docopts.md @@ -31,6 +31,7 @@ Usage: config mail [--mailuser=] [--mailpwd=] [--mailfrom=] [--mailto=] config volumes [--couchdb=] [--kafka=] [--pgvol=] [--storage=] [--alerting=] [--zookeeper=] [--redisvol=] [--mongodbvol=] [--etcdvol=] [--mvvol=] [--mvzookvol=] [--pulsarjournalvol=] [--pulsarledgelvol=] config controller [--javaopts=] [--loglevel=] [--replicas=] + config ingress [--class=] config invoker [--javaopts=] [--poolmemory=] [--timeoutsrun=] [--timeoutslogs=] [--loglevel=] [--replicas=] config limits [--time=