From 8c48a16777c509569a50972f0b4d8918ce5a0426 Mon Sep 17 00:00:00 2001 From: Bruno Salzano Date: Sun, 12 Jul 2026 20:07:04 +0000 Subject: [PATCH 1/3] fix: fix absolute path in run.sh starting the operator locally gave error "No such file or directory task: Failed to run task "run": task: Failed to run task "d:run": exit status 127", because of an absolute path inside run.sh --- TaskfileDev.yml | 2 ++ run.sh | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/TaskfileDev.yml b/TaskfileDev.yml index c7dc981e..d5c7503a 100644 --- a/TaskfileDev.yml +++ b/TaskfileDev.yml @@ -68,6 +68,8 @@ tasks: COUCHDB_SERVICE_HOST: "localhost" MINIO_API_HOST: "localhost" OW_CONTROLLER_HOST : "localhost" + VIRTUAL_ENV: + sh: poetry env info --path 2>/dev/null || echo "" OW_CONTROLLER_PORT : 3233 INVOKER_IMAGE: "{{.INVOKER_IMAGE}}" INVOKER_TAG: "{{.INVOKER_TAG}}" diff --git a/run.sh b/run.sh index 8a40774d..38f9f114 100755 --- a/run.sh +++ b/run.sh @@ -26,7 +26,11 @@ then mkdir -p deploy cd .. fi # start the operator if possible +KOPF="${VIRTUAL_ENV:-/home/nuvolaris/.venv}/bin/kopf" if kubectl -n nuvolaris get cm/config -then exec /home/nuvolaris/.venv/bin/kopf run -n nuvolaris -m nuvolaris nuvolaris/main.py nuvolaris/user_handlers.py nuvolaris/workflows.py "$@" +then if test -x "$KOPF" + then exec "$KOPF" run -n nuvolaris -m nuvolaris nuvolaris/main.py nuvolaris/user_handlers.py nuvolaris/workflows.py "$@" + else echo "kopf not found at $KOPF - run 'task setup' first." + fi else echo "You need to 'kubectl apply -f deploy/permissions' before starting the operator." fi From df3cb4bf4e167da932ade08de8cab39c9f0b125a Mon Sep 17 00:00:00 2001 From: Bruno Salzano Date: Sun, 12 Jul 2026 20:09:45 +0000 Subject: [PATCH 2/3] fix: path to check for db ready changed the path for wait_db_ready. The path without the final / led to a 301 and not a 200 --- actions/devel/download/nuvolaris/couchdb_util.py | 2 +- actions/devel/ferretdb/nuvolaris/couchdb_util.py | 2 +- actions/devel/minio/nuvolaris/couchdb_util.py | 2 +- actions/devel/psql/nuvolaris/couchdb_util.py | 2 +- actions/devel/redis/nuvolaris/couchdb_util.py | 2 +- actions/devel/upload/nuvolaris/couchdb_util.py | 2 +- nuvolaris/couchdb_util.py | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/actions/devel/download/nuvolaris/couchdb_util.py b/actions/devel/download/nuvolaris/couchdb_util.py index 7a18899b..b518a2dd 100644 --- a/actions/devel/download/nuvolaris/couchdb_util.py +++ b/actions/devel/download/nuvolaris/couchdb_util.py @@ -40,7 +40,7 @@ def wait_db_ready(self, max_seconds): session = req.Session() while delta < max_seconds: try: - r = session.get(f"{self.db_url}/_utils", timeout=5) + r = session.get(f"{self.db_url}/_utils/", timeout=5) logging.info(f"CouchDB.wait_db_ready() got response code = {r.status_code}") if r.status_code == 200: return True diff --git a/actions/devel/ferretdb/nuvolaris/couchdb_util.py b/actions/devel/ferretdb/nuvolaris/couchdb_util.py index 7a18899b..b518a2dd 100644 --- a/actions/devel/ferretdb/nuvolaris/couchdb_util.py +++ b/actions/devel/ferretdb/nuvolaris/couchdb_util.py @@ -40,7 +40,7 @@ def wait_db_ready(self, max_seconds): session = req.Session() while delta < max_seconds: try: - r = session.get(f"{self.db_url}/_utils", timeout=5) + r = session.get(f"{self.db_url}/_utils/", timeout=5) logging.info(f"CouchDB.wait_db_ready() got response code = {r.status_code}") if r.status_code == 200: return True diff --git a/actions/devel/minio/nuvolaris/couchdb_util.py b/actions/devel/minio/nuvolaris/couchdb_util.py index 7a18899b..b518a2dd 100644 --- a/actions/devel/minio/nuvolaris/couchdb_util.py +++ b/actions/devel/minio/nuvolaris/couchdb_util.py @@ -40,7 +40,7 @@ def wait_db_ready(self, max_seconds): session = req.Session() while delta < max_seconds: try: - r = session.get(f"{self.db_url}/_utils", timeout=5) + r = session.get(f"{self.db_url}/_utils/", timeout=5) logging.info(f"CouchDB.wait_db_ready() got response code = {r.status_code}") if r.status_code == 200: return True diff --git a/actions/devel/psql/nuvolaris/couchdb_util.py b/actions/devel/psql/nuvolaris/couchdb_util.py index 7a18899b..b518a2dd 100644 --- a/actions/devel/psql/nuvolaris/couchdb_util.py +++ b/actions/devel/psql/nuvolaris/couchdb_util.py @@ -40,7 +40,7 @@ def wait_db_ready(self, max_seconds): session = req.Session() while delta < max_seconds: try: - r = session.get(f"{self.db_url}/_utils", timeout=5) + r = session.get(f"{self.db_url}/_utils/", timeout=5) logging.info(f"CouchDB.wait_db_ready() got response code = {r.status_code}") if r.status_code == 200: return True diff --git a/actions/devel/redis/nuvolaris/couchdb_util.py b/actions/devel/redis/nuvolaris/couchdb_util.py index 7a18899b..b518a2dd 100644 --- a/actions/devel/redis/nuvolaris/couchdb_util.py +++ b/actions/devel/redis/nuvolaris/couchdb_util.py @@ -40,7 +40,7 @@ def wait_db_ready(self, max_seconds): session = req.Session() while delta < max_seconds: try: - r = session.get(f"{self.db_url}/_utils", timeout=5) + r = session.get(f"{self.db_url}/_utils/", timeout=5) logging.info(f"CouchDB.wait_db_ready() got response code = {r.status_code}") if r.status_code == 200: return True diff --git a/actions/devel/upload/nuvolaris/couchdb_util.py b/actions/devel/upload/nuvolaris/couchdb_util.py index 7a18899b..b518a2dd 100644 --- a/actions/devel/upload/nuvolaris/couchdb_util.py +++ b/actions/devel/upload/nuvolaris/couchdb_util.py @@ -40,7 +40,7 @@ def wait_db_ready(self, max_seconds): session = req.Session() while delta < max_seconds: try: - r = session.get(f"{self.db_url}/_utils", timeout=5) + r = session.get(f"{self.db_url}/_utils/", timeout=5) logging.info(f"CouchDB.wait_db_ready() got response code = {r.status_code}") if r.status_code == 200: return True diff --git a/nuvolaris/couchdb_util.py b/nuvolaris/couchdb_util.py index 7a18899b..b518a2dd 100644 --- a/nuvolaris/couchdb_util.py +++ b/nuvolaris/couchdb_util.py @@ -40,7 +40,7 @@ def wait_db_ready(self, max_seconds): session = req.Session() while delta < max_seconds: try: - r = session.get(f"{self.db_url}/_utils", timeout=5) + r = session.get(f"{self.db_url}/_utils/", timeout=5) logging.info(f"CouchDB.wait_db_ready() got response code = {r.status_code}") if r.status_code == 200: return True From 506e4bc0df4ecbcba5e2be238240f40f4e7f3597 Mon Sep 17 00:00:00 2001 From: Bruno Salzano Date: Sun, 12 Jul 2026 20:14:08 +0000 Subject: [PATCH 3/3] fix: invoker and operator image from env removed hardcoded values for cfg.get operator.image / operator.tag and invoker.image and invoker.tag. Config now check env vars and defaults to old images --- nuvolaris/couchdb.py | 4 ++-- nuvolaris/cronjob.py | 4 ++-- nuvolaris/enterprise_util.py | 7 ++++--- nuvolaris/invoker.py | 4 ++-- nuvolaris/openwhisk_enterprise.py | 4 ++-- nuvolaris/openwhisk_standalone.py | 7 ++++--- nuvolaris/quota_checker_job.py | 4 ++-- nuvolaris/templates/enterprise-sts.yaml | 2 +- nuvolaris/templates/invoker-sts.yaml | 2 +- nuvolaris/templates/standalone-sts.yaml | 2 +- nuvolaris/util.py | 4 ++-- nuvolaris/zookeeper.py | 2 +- tests/kind/nuvolaris_subject_test.ipy | 18 ++++++++++-------- tests/kind/whisk-minimal.yaml | 6 ++++-- tests/kind/whisk-slim.yaml | 8 ++++---- 15 files changed, 42 insertions(+), 36 deletions(-) diff --git a/nuvolaris/couchdb.py b/nuvolaris/couchdb.py index e6ebf6a8..6a38cf7e 100644 --- a/nuvolaris/couchdb.py +++ b/nuvolaris/couchdb.py @@ -43,8 +43,8 @@ def create(owner=None): user = f"db_username={u}" pasw = f"db_password={p}" - img = cfg.get('operator.image') or "missing-operator-image" - tag = cfg.get('operator.tag') or "missing-operator-tag" + img = cfg.get('operator.image', 'OPERATOR_IMAGE', "missing-operator-image") + tag = cfg.get('operator.tag', 'OPERATOR_TAG', "missing-operator-tag") image = f"{img}:{tag}" container_image = runtime in ['openshift'] and "ghcr.io/nuvolaris/couchdb:2.3.1-nuvolaris.23101915" or "apache/couchdb:2.3" diff --git a/nuvolaris/cronjob.py b/nuvolaris/cronjob.py index a0529d35..c02f3751 100644 --- a/nuvolaris/cronjob.py +++ b/nuvolaris/cronjob.py @@ -24,8 +24,8 @@ def create(owner=None): logging.info("creating cron") - img = cfg.get('operator.image') or "missing-operator-image" - tag = cfg.get('operator.tag') or "missing-operator-tag" + img = cfg.get("operator.image","OPERATOR_IMAGE", "ghcr.io/nuvolaris/openwhisk-controller") + tag = cfg.get("operator.tag","OPERATOR_TAG", "3.1.0-mastrogpt.2402101445") image = f"{img}:{tag}" logging.info(f"cron using image {image}") diff --git a/nuvolaris/enterprise_util.py b/nuvolaris/enterprise_util.py index fd0d0bb8..bd86f4f6 100644 --- a/nuvolaris/enterprise_util.py +++ b/nuvolaris/enterprise_util.py @@ -43,9 +43,10 @@ def get_invoker_image_data(data): img_data = util.parse_image(invoker_image) data['invoker_image'] = img_data["image"] data['invoker_tag'] = img_data["tag"] - else: - data['invoker_image'] = cfg.get("invoker.image") or "ghcr.io/nuvolaris/openwhisk-invoker" - data['invoker_tag'] = cfg.get("invoker.tag") or "3.1.0-mastrogpt.2402101445" + else: + #ghcr.io/nuvolaris/openwhisk-invoker:3.1.0-mastrogpt.2402101445 + data['invoker_image'] = cfg.get("invoker.image","INVOKER_IMAGE", "ghcr.io/nuvolaris/openwhisk-invoker") + data['invoker_tag'] = cfg.get("invoker.tag", "INVOKER_TAG", "3.1.0-mastrogpt.2402101445") def getEnterpriseControllerConfigData(): data = { diff --git a/nuvolaris/invoker.py b/nuvolaris/invoker.py index 92c673f1..0ae65dd4 100644 --- a/nuvolaris/invoker.py +++ b/nuvolaris/invoker.py @@ -39,8 +39,8 @@ def create(owner=None): if(data['affinity'] or data['tolerations']): tplp.append("affinity-tolerance-sts-invoker-attach.yaml") - config = kus.image(whisk_image, newTag=whisk_tag) - config += kus.patchTemplates("openwhisk-invoker", tplp, data) + logging.info(f"using invoker image {whisk_image}:{whisk_tag}") + config = kus.patchTemplates("openwhisk-invoker", tplp, data) spec = kus.kustom_list("openwhisk-invoker", config, templates=[], data=data) if owner: diff --git a/nuvolaris/openwhisk_enterprise.py b/nuvolaris/openwhisk_enterprise.py index 6fb6a193..1c4d5744 100644 --- a/nuvolaris/openwhisk_enterprise.py +++ b/nuvolaris/openwhisk_enterprise.py @@ -36,8 +36,8 @@ def create(owner=None): if(data['affinity'] or data['tolerations']): tplp.append("affinity-tolerance-sts-core-attach.yaml") - config = kus.image(whisk_image, newTag=whisk_tag) - config += kus.patchTemplates("openwhisk-enterprise", tplp, data) + logging.info(f"using controller image {whisk_image}:{whisk_tag}") + config = kus.patchTemplates("openwhisk-enterprise", tplp, data) spec = kus.kustom_list("openwhisk-enterprise", config, templates=[], data=data) if owner: diff --git a/nuvolaris/openwhisk_standalone.py b/nuvolaris/openwhisk_standalone.py index f44bc3dd..1d53d431 100644 --- a/nuvolaris/openwhisk_standalone.py +++ b/nuvolaris/openwhisk_standalone.py @@ -31,13 +31,14 @@ def create(owner=None): whisk_image = data["controller_image"] whisk_tag = data["controller_tag"] - config = kus.image(whisk_image, newTag=whisk_tag) - + + logging.info(f"using controller image {whisk_image}:{whisk_tag}") + tplp = ["standalone-sts.yaml"] if(data['affinity'] or data['tolerations']): tplp.append("affinity-tolerance-sts-core-attach.yaml") - config += kus.patchTemplates("openwhisk-standalone", tplp, data) + config = kus.patchTemplates("openwhisk-standalone", tplp, data) spec = kus.kustom_list("openwhisk-standalone", config, templates=[], data=data) if owner: diff --git a/nuvolaris/quota_checker_job.py b/nuvolaris/quota_checker_job.py index 534d447a..d67c5526 100644 --- a/nuvolaris/quota_checker_job.py +++ b/nuvolaris/quota_checker_job.py @@ -24,8 +24,8 @@ def create(owner=None): logging.info("creating quota cheker scheduled job") - img = cfg.get('operator.image') or "missing-operator-image" - tag = cfg.get('operator.tag') or "missing-operator-tag" + img = cfg.get('operator.image','OPERATOR_IMAGE') or "missing-operator-image" + tag = cfg.get('operator.tag','OPERATOR_TAG') or "missing-operator-tag" image = f"{img}:{tag}" logging.info("quota job using image %s", image) diff --git a/nuvolaris/templates/enterprise-sts.yaml b/nuvolaris/templates/enterprise-sts.yaml index db8320a7..3511f810 100644 --- a/nuvolaris/templates/enterprise-sts.yaml +++ b/nuvolaris/templates/enterprise-sts.yaml @@ -48,7 +48,7 @@ spec: containers: - name: controller imagePullPolicy: "IfNotPresent" - image: "ghcr.io/nuvolaris/openwhisk-controller:0.3.0-morpheus.22122609" + image: "{{controller_image}}:{{controller_tag}}" command: ["/bin/bash", "-c", "/init.sh `hostname | awk -F '-' '{print $NF}'`"] ports: - name: controller diff --git a/nuvolaris/templates/invoker-sts.yaml b/nuvolaris/templates/invoker-sts.yaml index 1f27bcf9..e84a38db 100644 --- a/nuvolaris/templates/invoker-sts.yaml +++ b/nuvolaris/templates/invoker-sts.yaml @@ -47,7 +47,7 @@ spec: command: ['sh', '-c', "until nslookup kafka.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for kafka; sleep 2; done"] containers: - name: invoker - image: "ghcr.io/nuvolaris/openwhisk-invoker:0.3.0-morpheus.22122609" + image: "{{invoker_image}}:{{invoker_tag}}" imagePullPolicy: "IfNotPresent" command: [ "/bin/bash", "-c", "/init.sh --uniqueName $INVOKER_NAME" ] #command: ["sleep", "inf"] diff --git a/nuvolaris/templates/standalone-sts.yaml b/nuvolaris/templates/standalone-sts.yaml index 2ee9da87..611f5028 100644 --- a/nuvolaris/templates/standalone-sts.yaml +++ b/nuvolaris/templates/standalone-sts.yaml @@ -45,7 +45,7 @@ spec: containers: - name: controller imagePullPolicy: "IfNotPresent" - image: "ghcr.io/nuvolaris/openwhisk-controller:0.3.0-morpheus.22122609" + image: "{{controller_image}}:{{controller_tag}}" command: ["/bin/bash", "-c", "/init.sh `hostname | awk -F '-' '{print $NF}'`"] ports: - name: controller diff --git a/nuvolaris/util.py b/nuvolaris/util.py index b6168516..fae2c7f5 100644 --- a/nuvolaris/util.py +++ b/nuvolaris/util.py @@ -345,8 +345,8 @@ def get_controller_image_data(data): data['controller_image'] = img_data["image"] data['controller_tag'] = img_data["tag"] else: - data['controller_image'] = cfg.get("controller.image") or "ghcr.io/nuvolaris/openwhisk-controller" - data['controller_tag'] = cfg.get("controller.tag") or "3.1.0-mastrogpt.2402101445" + data['controller_image'] = cfg.get("controller.image","CONTROLLER_IMAGE", "ghcr.io/nuvolaris/openwhisk-controller") + data['controller_tag'] = cfg.get("controller.tag","CONTROLLER_TAG", "3.1.0-mastrogpt.2402101445") # return configuration parameters for the standalone controller def get_standalone_config_data(): diff --git a/nuvolaris/zookeeper.py b/nuvolaris/zookeeper.py index b7edb6af..0ba51979 100644 --- a/nuvolaris/zookeeper.py +++ b/nuvolaris/zookeeper.py @@ -39,7 +39,7 @@ def get_zookeeper_connect_data(): return None def create(owner=None): - logging.info(f"*** configuring zookeeper") + logging.info("*** configuring zookeeper") data = cfg_util.get_zookeeper_config_data() diff --git a/tests/kind/nuvolaris_subject_test.ipy b/tests/kind/nuvolaris_subject_test.ipy index 6436dc8a..242b67d7 100644 --- a/tests/kind/nuvolaris_subject_test.ipy +++ b/tests/kind/nuvolaris_subject_test.ipy @@ -16,23 +16,25 @@ # under the License. # -!kubectl -n nuvolaris delete all --all -!kubectl -n nuvolaris delete pvc --all - import nuvolaris.couchdb as cdb import nuvolaris.couchdb_util as cdbu import nuvolaris.testutil as tu import nuvolaris.config as cfg import nuvolaris.kube as kube +import nuvolaris.testutil as tu import time from kopf.testing import KopfRunner +tu.run_proc("kubectl -n nuvolaris delete all --all") +tu.run_proc("kubectl -n nuvolaris delete pvc --all") + + assert(cfg.configure(tu.load_sample_config())) assert(cfg.detect_labels()["nuvolaris.kube"] == "kind") assert(cfg.detect_storage()["nuvolaris.storageclass"] == "standard") assert(cfg.put("couchdb.host", "localhost")) -!kubectl apply -f tests/kind/whisk.yaml +tu.run_proc("kubectl apply -f tests/kind/whisk.yaml") wsk = kube.get("wsk/controller") cdb.create(wsk) @@ -51,17 +53,17 @@ assert(cdb.add_initial_subjects(db)) with KopfRunner(['run', '-A', '--verbose', 'nuvolaris/user_handlers.py']) as runner: # do something while the operator is running. - !kubectl apply -f tests/whisk-user.yaml + tu.run_proc("kubectl apply -f tests/whisk-user.yaml") time.sleep(1) # give it some time to react and to sleep and to retry wsku = kube.get("wsku/franztt") assert(wsku['spec']) - !kubectl delete -f tests/whisk-user.yaml + tu.run_proc("kubectl delete -f tests/whisk-user.yaml") time.sleep(1) # give it some time to react assert runner.exit_code == 0 assert runner.exception is None -!kubectl -n nuvolaris delete all --all -!kubectl -n nuvolaris delete pvc --all +tu.run_proc("kubectl -n nuvolaris delete all --all") +tu.run_proc("kubectl -n nuvolaris delete pvc --all") diff --git a/tests/kind/whisk-minimal.yaml b/tests/kind/whisk-minimal.yaml index c3fc351a..6b8afcce 100644 --- a/tests/kind/whisk-minimal.yaml +++ b/tests/kind/whisk-minimal.yaml @@ -25,11 +25,13 @@ spec: # start openwhisk controller openwhisk: true # start openwhisk invoker - invoker: false + invoker: true # start couchdb couchdb: true # start kafka - kafka: false + kafka: true + # zookeeper enabled or not + zookeeper: true # start mongodb mongodb: false # start redis diff --git a/tests/kind/whisk-slim.yaml b/tests/kind/whisk-slim.yaml index f6f3f4a9..6af60808 100644 --- a/tests/kind/whisk-slim.yaml +++ b/tests/kind/whisk-slim.yaml @@ -41,19 +41,19 @@ spec: # tls enabled or not tls: false # minio enabled or not - minio: true + minio: false # minio static enabled or not static: true # postgres enabled or not postgres: true # etcd enabled or not - etcd: true + etcd: false # milvus enabled or not - milvus: true + milvus: false # registry enabled or not registry: false # seaweedfs enabled or not - seaweedfs: false + seaweedfs: true openwhisk: namespaces: whisk-system: 789c46b1-71f6-4ed5-8c54-816aa4f8c502:abczO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP