You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openserverless.apache.org by fr...@apache.org on 2024/08/06 13:40:32 UTC
(openserverless-operator) branch main updated (370d717 -> af0d82b)
This is an automated email from the ASF dual-hosted git repository.
franztt pushed a change to branch main
in repository https://gitbox.apache.org/repos/asf/openserverless-operator.git
discard 370d717 Merge pull request #14 from francescotimperi/main
discard bb7e481 chore: aligning tag version in build process
discard f3cfb39 fix: cron action executor does not need to pass ow action parameters
discard e2a0aa4 Merge pull request #13 from francescotimperi/main
discard a288d1f chore: setup multiplatform images
discard 94d0333 chore: added TAG extraction steps
discard 1878575 fix: update to licenserc file
discard 0cfc872 chore: github actions fix
discard e59decf chore: preparing github actions to build operator
discard 1cd69ef Merge pull request #12 from francescotimperi/main
discard 04dc560 chore: updated version tag and added DISCLAIMER and NOTICE file for incubating ASF projects
discard 843d6d2 ignoring .direnv
discard e4ee26b Merge pull request #9 from francescotimperi/handler-fix
discard 510a826 chore: cleanup unused demos
discard a9f4ca4 Merge pull request #8 from francescotimperi/handler-fix
new 8c21a28 Merge pull request #8 from francescotimperi/handler-fix
new d2a919d chore: cleanup unused demos
new 5978eb4 Merge pull request #9 from francescotimperi/handler-fix
new 11e176d ignoring .direnv
new a705f51 chore: updated version tag and added DISCLAIMER and NOTICE file for incubating ASF projects
new ad9b05d Merge pull request #12 from francescotimperi/main
new 18ce9f5 chore: preparing github actions to build operator
new a5ae583 chore: github actions fix
new 7b8f1c1 fix: update to licenserc file
new 29a4251 chore: added TAG extraction steps
new 6d20f91 chore: setup multiplatform images
new f029dae Merge pull request #13 from francescotimperi/main
new bf78d79 fix: cron action executor does not need to pass ow action parameters
new 1757d71 chore: aligning tag version in build process
new af0d82b Merge pull request #14 from francescotimperi/main
This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version. This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:
* -- * -- B -- O -- O -- O (370d717)
\
N -- N -- N refs/heads/main (af0d82b)
You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.
Any revisions marked "omit" are not gone; other references still
refer to them. Any revisions marked "discard" are gone forever.
The 24 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
(openserverless-operator) 23/24: chore: aligning tag version in build process
Posted by fr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
franztt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openserverless-operator.git
commit 1757d7153682b9d355b72b3c829aaad152aef7e8
Author: francescotimperi <fr...@gmail.com>
AuthorDate: Sat Jul 27 16:00:18 2024 +0000
chore: aligning tag version in build process
---
.github/workflows/image.yml | 6 +++++-
Dockerfile | 4 ++--
Taskfile.yml | 2 +-
3 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/image.yml b/.github/workflows/image.yml
index 60c2732..a544774 100644
--- a/.github/workflows/image.yml
+++ b/.github/workflows/image.yml
@@ -62,7 +62,11 @@ jobs:
- name: Assign TAG from push
if: ${{ github.event_name != 'pull_request' }}
run: |
- echo "IMG_TAG=$(git describe --tags --abbrev=0)" >> "$GITHUB_ENV"
+ echo "IMG_TAG=$(git describe --tags --abbrev=0)" >> "$GITHUB_ENV"
+ - name: Assign Custom Image Name
+ if: ${{ github.repository_owner != 'apache'}}
+ run: |
+ echo "MY_OPERATOR_IMAGE=${{ vars.MY_OPERATOR_IMAGE }}" >> "$GITHUB_ENV"
- name: Registry login
uses: docker/login-action@v3
with:
diff --git a/Dockerfile b/Dockerfile
index 92ecf70..5ba6731 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -20,8 +20,8 @@ ENV CONTROLLER_IMAGE=ghcr.io/nuvolaris/openwhisk-controller
ENV CONTROLLER_TAG=3.1.0-mastrogpt.2402101445
ENV INVOKER_IMAGE=ghcr.io/nuvolaris/openwhisk-invoker
ENV INVOKER_TAG=3.1.0-mastrogpt.2402101445
-ARG OPERATOR_IMAGE_DEFAULT=registry.hub.docker.com/apacheopenserverless/openserverless-operator
-ARG OPERATOR_TAG_DEFAULT=0.1-snapshot.2309191654
+ARG OPERATOR_IMAGE_DEFAULT=registry.hub.docker.com/apache/openserverless-operator
+ARG OPERATOR_TAG_DEFAULT=0.1.0-testing.2309191654
ENV OPERATOR_IMAGE=${OPERATOR_IMAGE_DEFAULT}
ENV OPERATOR_TAG=${OPERATOR_TAG_DEFAULT}
diff --git a/Taskfile.yml b/Taskfile.yml
index 5d4ed56..e8c91a9 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -22,7 +22,7 @@ vars:
N: ""
T: ""
P: ""
- BASETAG: 0.1-snapshot
+ BASETAG: 0.1.0-testing
KUBE:
sh: ./detect.sh
NS: "nuvolaris"
(openserverless-operator) 09/24: fix: pg quota checker was using wrong db name when checking ferretdb ones
Posted by fr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
franztt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openserverless-operator.git
commit 4d8ee0c588a80ce714b9ab7ad423b5a09aa09fb4
Author: francescotimperi <fr...@gmail.com>
AuthorDate: Wed Jul 17 22:17:36 2024 +0000
fix: pg quota checker was using wrong db name when checking ferretdb ones
---
nuvolaris/quota_checker.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nuvolaris/quota_checker.py b/nuvolaris/quota_checker.py
index 02df4dc..a09fd51 100644
--- a/nuvolaris/quota_checker.py
+++ b/nuvolaris/quota_checker.py
@@ -81,7 +81,7 @@ def check_pg_quota(pg_client:PostgresClient, pg_dbsize_list, pg_wsku, check_ferr
quota_annotation = check_ferretdb and FERRRET_DB_QUOTA_ANNOTATION or POSTGRES_DB_QUOTA_ANNOTATION
quota_applied = "false"
- pg_db= check_ferretdb and f"spec['mongodb']['database']_ferretdb" or spec['postgres']['database']
+ pg_db= check_ferretdb and f"{spec['mongodb']['database']}_ferretdb" or spec['postgres']['database']
pg_db_quota = check_ferretdb and int(spec['mongodb']['quota'])*1014*1024 or int(spec['postgres']['quota'])*1024*1024
# Check if the quota annotations has been already applied
(openserverless-operator) 11/24: chore: cleanup unused demos
Posted by fr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
franztt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openserverless-operator.git
commit d2a919d42b96d220cb119df12dd05d09d1078ca8
Author: francescotimperi <fr...@gmail.com>
AuthorDate: Thu Jul 18 21:17:01 2024 +0000
chore: cleanup unused demos
---
demos/hellonode/.gitignore | 19 -
demos/hellonode/Dockerfile | 26 -
demos/hellonode/app.js | 30 -
demos/hellonode/deploy.yaml | 55 -
demos/hellonode/package-lock.json | 1018 -----------------
demos/hellonode/package.json | 15 -
demos/operator/demo-crd.yaml | 71 --
demos/operator/demo-deployment.yaml | 37 -
demos/operator/demo-ns.yaml | 22 -
demos/operator/demo-obj.yaml | 26 -
demos/operator/demo1.py | 39 -
demos/operator/demo2.py | 61 -
demos/operator/demomock.py | 27 -
demos/operator/obj.yaml | 32 -
demos/operator/patch.yaml | 24 -
demos/operator/poetry.lock | 1179 --------------------
demos/operator/pyproject.toml | 35 -
demos/operator/transcript.txt | 78 --
demos/velero/Taskfile.yml | 154 ---
demos/velero/values.yaml | 538 ---------
deploy/_deprecated/preload-runtime-go117.yaml | 29 -
deploy/_deprecated/preload-runtime-nodej14.yaml | 28 -
deploy/_deprecated/preload-runtime-python37.yaml | 29 -
deploy/_deprecated/standalone-kcf.yaml | 77 --
deploy/_deprecated/standalone-lb.yaml | 29 -
deploy/_deprecated/standalone-np.yaml | 58 -
deploy/_deprecated/standalone-set.yaml | 61 -
deploy/_deprecated/standalone-test-in-https.yaml | 47 -
enterprise-util/couchdb/couchdb-nuvolaris-app.yaml | 42 -
.../couchdb/couchdb-nuvolaris-org-ingress.yaml | 42 -
enterprise-util/minio/minio-aks-ingress.yaml | 42 -
.../minio/minio-nuvolaris-org-ingress.yaml | 42 -
enterprise-util/minio/minio-saa1-ingress.yaml | 37 -
.../monitoring/prometheus-localhost.yaml | 37 -
.../prometheus-mistergpt-app-ingress.yaml | 37 -
.../prometheus-nuvolaris-app-ingress.yaml | 42 -
.../prometheus-nuvolaris-org-ingress.yaml | 42 -
enterprise/.gitkeep | 0
38 files changed, 4207 deletions(-)
diff --git a/demos/hellonode/.gitignore b/demos/hellonode/.gitignore
deleted file mode 100644
index 220df51..0000000
--- a/demos/hellonode/.gitignore
+++ /dev/null
@@ -1,19 +0,0 @@
-# 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.
-
-node_modules
-
diff --git a/demos/hellonode/Dockerfile b/demos/hellonode/Dockerfile
deleted file mode 100644
index 87488a7..0000000
--- a/demos/hellonode/Dockerfile
+++ /dev/null
@@ -1,26 +0,0 @@
-# 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.
-
-FROM alpine
-RUN apk add nodejs npm
-RUN adduser -D nuv
-WORKDIR /home/nuv
-USER nuv
-ADD app.js package.json /home/nuv/
-RUN npm install
-EXPOSE 8080
-CMD node app.js
\ No newline at end of file
diff --git a/demos/hellonode/app.js b/demos/hellonode/app.js
deleted file mode 100644
index d7b1c79..0000000
--- a/demos/hellonode/app.js
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.
- */
-
-const express = require('express')
-const app = express()
-const port = 8080
-
-app.get('/', (req, res) => {
- res.send('Hello World!')
-})
-
-app.listen(port, () => {
- console.log(`Example app listening on port ${port}`)
-})
diff --git a/demos/hellonode/deploy.yaml b/demos/hellonode/deploy.yaml
deleted file mode 100644
index 50d8370..0000000
--- a/demos/hellonode/deploy.yaml
+++ /dev/null
@@ -1,55 +0,0 @@
-# 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.
-
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: hellonode
- labels:
- app: hellonode
-spec:
- replicas: 1
- selector:
- matchLabels:
- app: hellonode
- template:
- metadata:
- labels:
- app: hellonode
- spec:
- containers:
- - name: podcool
- image: ghcr.io/msciabarra/hellonode
- ports:
- - containerPort: 8080
----
-apiVersion: v1
-kind: Service
-metadata:
- name: hellonode
- labels:
- app: hellonode
-spec:
- type: NodePort
- ports:
- - port: 8080
- targetPort: 8080
- nodePort: 30880
- protocol: TCP
- selector:
- app: hellonode
diff --git a/demos/hellonode/package-lock.json b/demos/hellonode/package-lock.json
deleted file mode 100644
index ba48099..0000000
--- a/demos/hellonode/package-lock.json
+++ /dev/null
@@ -1,1018 +0,0 @@
-{
- "name": "boh",
- "version": "1.0.0",
- "lockfileVersion": 2,
- "requires": true,
- "packages": {
- "": {
- "name": "boh",
- "version": "1.0.0",
- "license": "ISC",
- "dependencies": {
- "express": "^4.18.1"
- }
- },
- "node_modules/accepts": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
- "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
- "dependencies": {
- "mime-types": "~2.1.34",
- "negotiator": "0.6.3"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/array-flatten": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
- "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
- },
- "node_modules/body-parser": {
- "version": "1.20.0",
- "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz",
- "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==",
- "dependencies": {
- "bytes": "3.1.2",
- "content-type": "~1.0.4",
- "debug": "2.6.9",
- "depd": "2.0.0",
- "destroy": "1.2.0",
- "http-errors": "2.0.0",
- "iconv-lite": "0.4.24",
- "on-finished": "2.4.1",
- "qs": "6.10.3",
- "raw-body": "2.5.1",
- "type-is": "~1.6.18",
- "unpipe": "1.0.0"
- },
- "engines": {
- "node": ">= 0.8",
- "npm": "1.2.8000 || >= 1.4.16"
- }
- },
- "node_modules/bytes": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
- "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/call-bind": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
- "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
- "dependencies": {
- "function-bind": "^1.1.1",
- "get-intrinsic": "^1.0.2"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/content-disposition": {
- "version": "0.5.4",
- "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
- "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
- "dependencies": {
- "safe-buffer": "5.2.1"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/content-type": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
- "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/cookie": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
- "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/cookie-signature": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
- "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
- },
- "node_modules/debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dependencies": {
- "ms": "2.0.0"
- }
- },
- "node_modules/depd": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
- "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/destroy": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
- "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
- "engines": {
- "node": ">= 0.8",
- "npm": "1.2.8000 || >= 1.4.16"
- }
- },
- "node_modules/ee-first": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
- "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
- },
- "node_modules/encodeurl": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
- "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/escape-html": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
- "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
- },
- "node_modules/etag": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
- "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/express": {
- "version": "4.18.1",
- "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz",
- "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==",
- "dependencies": {
- "accepts": "~1.3.8",
- "array-flatten": "1.1.1",
- "body-parser": "1.20.0",
- "content-disposition": "0.5.4",
- "content-type": "~1.0.4",
- "cookie": "0.5.0",
- "cookie-signature": "1.0.6",
- "debug": "2.6.9",
- "depd": "2.0.0",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "etag": "~1.8.1",
- "finalhandler": "1.2.0",
- "fresh": "0.5.2",
- "http-errors": "2.0.0",
- "merge-descriptors": "1.0.1",
- "methods": "~1.1.2",
- "on-finished": "2.4.1",
- "parseurl": "~1.3.3",
- "path-to-regexp": "0.1.7",
- "proxy-addr": "~2.0.7",
- "qs": "6.10.3",
- "range-parser": "~1.2.1",
- "safe-buffer": "5.2.1",
- "send": "0.18.0",
- "serve-static": "1.15.0",
- "setprototypeof": "1.2.0",
- "statuses": "2.0.1",
- "type-is": "~1.6.18",
- "utils-merge": "1.0.1",
- "vary": "~1.1.2"
- },
- "engines": {
- "node": ">= 0.10.0"
- }
- },
- "node_modules/finalhandler": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
- "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
- "dependencies": {
- "debug": "2.6.9",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "on-finished": "2.4.1",
- "parseurl": "~1.3.3",
- "statuses": "2.0.1",
- "unpipe": "~1.0.0"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/forwarded": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
- "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/fresh": {
- "version": "0.5.2",
- "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
- "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/function-bind": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
- "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
- },
- "node_modules/get-intrinsic": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz",
- "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==",
- "dependencies": {
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.3"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "dependencies": {
- "function-bind": "^1.1.1"
- },
- "engines": {
- "node": ">= 0.4.0"
- }
- },
- "node_modules/has-symbols": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
- "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/http-errors": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
- "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
- "dependencies": {
- "depd": "2.0.0",
- "inherits": "2.0.4",
- "setprototypeof": "1.2.0",
- "statuses": "2.0.1",
- "toidentifier": "1.0.1"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/iconv-lite": {
- "version": "0.4.24",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
- "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
- "dependencies": {
- "safer-buffer": ">= 2.1.2 < 3"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
- },
- "node_modules/ipaddr.js": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
- "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/media-typer": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
- "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/merge-descriptors": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
- "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
- },
- "node_modules/methods": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
- "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/mime": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
- "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
- "bin": {
- "mime": "cli.js"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/mime-db": {
- "version": "1.52.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/mime-types": {
- "version": "2.1.35",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
- "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
- "dependencies": {
- "mime-db": "1.52.0"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
- },
- "node_modules/negotiator": {
- "version": "0.6.3",
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
- "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/object-inspect": {
- "version": "1.12.2",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
- "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/on-finished": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
- "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
- "dependencies": {
- "ee-first": "1.1.1"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/parseurl": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
- "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/path-to-regexp": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
- "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
- },
- "node_modules/proxy-addr": {
- "version": "2.0.7",
- "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
- "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
- "dependencies": {
- "forwarded": "0.2.0",
- "ipaddr.js": "1.9.1"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/qs": {
- "version": "6.10.3",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz",
- "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==",
- "dependencies": {
- "side-channel": "^1.0.4"
- },
- "engines": {
- "node": ">=0.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/range-parser": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
- "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/raw-body": {
- "version": "2.5.1",
- "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
- "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
- "dependencies": {
- "bytes": "3.1.2",
- "http-errors": "2.0.0",
- "iconv-lite": "0.4.24",
- "unpipe": "1.0.0"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/safe-buffer": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ]
- },
- "node_modules/safer-buffer": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
- },
- "node_modules/send": {
- "version": "0.18.0",
- "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
- "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
- "dependencies": {
- "debug": "2.6.9",
- "depd": "2.0.0",
- "destroy": "1.2.0",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "etag": "~1.8.1",
- "fresh": "0.5.2",
- "http-errors": "2.0.0",
- "mime": "1.6.0",
- "ms": "2.1.3",
- "on-finished": "2.4.1",
- "range-parser": "~1.2.1",
- "statuses": "2.0.1"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/send/node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
- },
- "node_modules/serve-static": {
- "version": "1.15.0",
- "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
- "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
- "dependencies": {
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "parseurl": "~1.3.3",
- "send": "0.18.0"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/setprototypeof": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
- "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
- },
- "node_modules/side-channel": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
- "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
- "dependencies": {
- "call-bind": "^1.0.0",
- "get-intrinsic": "^1.0.2",
- "object-inspect": "^1.9.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/statuses": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
- "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/toidentifier": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
- "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
- "engines": {
- "node": ">=0.6"
- }
- },
- "node_modules/type-is": {
- "version": "1.6.18",
- "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
- "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
- "dependencies": {
- "media-typer": "0.3.0",
- "mime-types": "~2.1.24"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/unpipe": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
- "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/utils-merge": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
- "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
- "engines": {
- "node": ">= 0.4.0"
- }
- },
- "node_modules/vary": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
- "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
- "engines": {
- "node": ">= 0.8"
- }
- }
- },
- "dependencies": {
- "accepts": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
- "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
- "requires": {
- "mime-types": "~2.1.34",
- "negotiator": "0.6.3"
- }
- },
- "array-flatten": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
- "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
- },
- "body-parser": {
- "version": "1.20.0",
- "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz",
- "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==",
- "requires": {
- "bytes": "3.1.2",
- "content-type": "~1.0.4",
- "debug": "2.6.9",
- "depd": "2.0.0",
- "destroy": "1.2.0",
- "http-errors": "2.0.0",
- "iconv-lite": "0.4.24",
- "on-finished": "2.4.1",
- "qs": "6.10.3",
- "raw-body": "2.5.1",
- "type-is": "~1.6.18",
- "unpipe": "1.0.0"
- }
- },
- "bytes": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
- "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="
- },
- "call-bind": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
- "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
- "requires": {
- "function-bind": "^1.1.1",
- "get-intrinsic": "^1.0.2"
- }
- },
- "content-disposition": {
- "version": "0.5.4",
- "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
- "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
- "requires": {
- "safe-buffer": "5.2.1"
- }
- },
- "content-type": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
- "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
- },
- "cookie": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
- "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="
- },
- "cookie-signature": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
- "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
- },
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "requires": {
- "ms": "2.0.0"
- }
- },
- "depd": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
- "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
- },
- "destroy": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
- "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="
- },
- "ee-first": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
- "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
- },
- "encodeurl": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
- "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="
- },
- "escape-html": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
- "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
- },
- "etag": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
- "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="
- },
- "express": {
- "version": "4.18.1",
- "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz",
- "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==",
- "requires": {
- "accepts": "~1.3.8",
- "array-flatten": "1.1.1",
- "body-parser": "1.20.0",
- "content-disposition": "0.5.4",
- "content-type": "~1.0.4",
- "cookie": "0.5.0",
- "cookie-signature": "1.0.6",
- "debug": "2.6.9",
- "depd": "2.0.0",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "etag": "~1.8.1",
- "finalhandler": "1.2.0",
- "fresh": "0.5.2",
- "http-errors": "2.0.0",
- "merge-descriptors": "1.0.1",
- "methods": "~1.1.2",
- "on-finished": "2.4.1",
- "parseurl": "~1.3.3",
- "path-to-regexp": "0.1.7",
- "proxy-addr": "~2.0.7",
- "qs": "6.10.3",
- "range-parser": "~1.2.1",
- "safe-buffer": "5.2.1",
- "send": "0.18.0",
- "serve-static": "1.15.0",
- "setprototypeof": "1.2.0",
- "statuses": "2.0.1",
- "type-is": "~1.6.18",
- "utils-merge": "1.0.1",
- "vary": "~1.1.2"
- }
- },
- "finalhandler": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
- "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
- "requires": {
- "debug": "2.6.9",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "on-finished": "2.4.1",
- "parseurl": "~1.3.3",
- "statuses": "2.0.1",
- "unpipe": "~1.0.0"
- }
- },
- "forwarded": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
- "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="
- },
- "fresh": {
- "version": "0.5.2",
- "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
- "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="
- },
- "function-bind": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
- "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
- },
- "get-intrinsic": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz",
- "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==",
- "requires": {
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.3"
- }
- },
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "requires": {
- "function-bind": "^1.1.1"
- }
- },
- "has-symbols": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
- "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
- },
- "http-errors": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
- "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
- "requires": {
- "depd": "2.0.0",
- "inherits": "2.0.4",
- "setprototypeof": "1.2.0",
- "statuses": "2.0.1",
- "toidentifier": "1.0.1"
- }
- },
- "iconv-lite": {
- "version": "0.4.24",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
- "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
- "requires": {
- "safer-buffer": ">= 2.1.2 < 3"
- }
- },
- "inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
- },
- "ipaddr.js": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
- "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
- },
- "media-typer": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
- "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="
- },
- "merge-descriptors": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
- "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
- },
- "methods": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
- "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="
- },
- "mime": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
- "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
- },
- "mime-db": {
- "version": "1.52.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
- },
- "mime-types": {
- "version": "2.1.35",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
- "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
- "requires": {
- "mime-db": "1.52.0"
- }
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
- },
- "negotiator": {
- "version": "0.6.3",
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
- "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
- },
- "object-inspect": {
- "version": "1.12.2",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
- "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ=="
- },
- "on-finished": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
- "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
- "requires": {
- "ee-first": "1.1.1"
- }
- },
- "parseurl": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
- "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
- },
- "path-to-regexp": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
- "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
- },
- "proxy-addr": {
- "version": "2.0.7",
- "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
- "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
- "requires": {
- "forwarded": "0.2.0",
- "ipaddr.js": "1.9.1"
- }
- },
- "qs": {
- "version": "6.10.3",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz",
- "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==",
- "requires": {
- "side-channel": "^1.0.4"
- }
- },
- "range-parser": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
- "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
- },
- "raw-body": {
- "version": "2.5.1",
- "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
- "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
- "requires": {
- "bytes": "3.1.2",
- "http-errors": "2.0.0",
- "iconv-lite": "0.4.24",
- "unpipe": "1.0.0"
- }
- },
- "safe-buffer": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
- },
- "safer-buffer": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
- },
- "send": {
- "version": "0.18.0",
- "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
- "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
- "requires": {
- "debug": "2.6.9",
- "depd": "2.0.0",
- "destroy": "1.2.0",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "etag": "~1.8.1",
- "fresh": "0.5.2",
- "http-errors": "2.0.0",
- "mime": "1.6.0",
- "ms": "2.1.3",
- "on-finished": "2.4.1",
- "range-parser": "~1.2.1",
- "statuses": "2.0.1"
- },
- "dependencies": {
- "ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
- }
- }
- },
- "serve-static": {
- "version": "1.15.0",
- "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
- "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
- "requires": {
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "parseurl": "~1.3.3",
- "send": "0.18.0"
- }
- },
- "setprototypeof": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
- "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
- },
- "side-channel": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
- "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
- "requires": {
- "call-bind": "^1.0.0",
- "get-intrinsic": "^1.0.2",
- "object-inspect": "^1.9.0"
- }
- },
- "statuses": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
- "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
- },
- "toidentifier": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
- "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
- },
- "type-is": {
- "version": "1.6.18",
- "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
- "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
- "requires": {
- "media-typer": "0.3.0",
- "mime-types": "~2.1.24"
- }
- },
- "unpipe": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
- "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="
- },
- "utils-merge": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
- "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="
- },
- "vary": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
- "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
- }
- }
-}
diff --git a/demos/hellonode/package.json b/demos/hellonode/package.json
deleted file mode 100644
index 81ab576..0000000
--- a/demos/hellonode/package.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "name": "boh",
- "version": "1.0.0",
- "description": "",
- "main": "app.js",
- "scripts": {
- "build": "docker build . -t ghcr.io/msciabarra/hellonode",
- "push": "docker push ghcr.io/msciabarra/hellonode"
- },
- "author": "",
- "license": "ISC",
- "dependencies": {
- "express": "^4.18.1"
- }
-}
diff --git a/demos/operator/demo-crd.yaml b/demos/operator/demo-crd.yaml
deleted file mode 100644
index 6d20c1d..0000000
--- a/demos/operator/demo-crd.yaml
+++ /dev/null
@@ -1,71 +0,0 @@
-# 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.
-#
-# A demo CRD for the Kopf example operators.
-# 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.
-#
-# A demo CRD for the Kopf example operators.
----
-apiVersion: apiextensions.k8s.io/v1
-kind: CustomResourceDefinition
-metadata:
- name: samples.nuvolaris.org
-spec:
- scope: Namespaced
- group: nuvolaris.org
- names:
- kind: Sample
- plural: samples
- singular: sample
- shortNames:
- - sam
- versions:
- - name: v1
- served: true
- storage: true
- subresources: { status: { } } # comment/uncomment for experiments
- schema:
- openAPIV3Schema:
- type: object
- properties:
- spec:
- type: object
- x-kubernetes-preserve-unknown-fields: true
- status:
- type: object
- x-kubernetes-preserve-unknown-fields: true
- additionalPrinterColumns:
- - name: Message
- type: string
- priority: 0
- jsonPath: .status.sample_create.message
- description: As returned from the handler (sometimes).
diff --git a/demos/operator/demo-deployment.yaml b/demos/operator/demo-deployment.yaml
deleted file mode 100644
index 0135563..0000000
--- a/demos/operator/demo-deployment.yaml
+++ /dev/null
@@ -1,37 +0,0 @@
-# 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.
-#
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: demo-deploy
- namespace: demo
-spec:
- replicas: 3
- selector:
- matchLabels:
- app: nginx
- template:
- metadata:
- labels:
- app: nginx
- spec:
- containers:
- - name: nginx
- image: nginx
- ports:
- - containerPort: 80
diff --git a/demos/operator/demo-ns.yaml b/demos/operator/demo-ns.yaml
deleted file mode 100644
index 4855cec..0000000
--- a/demos/operator/demo-ns.yaml
+++ /dev/null
@@ -1,22 +0,0 @@
-# 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.
-#
----
-apiVersion: v1
-kind: Namespace
-metadata:
- name: demo
diff --git a/demos/operator/demo-obj.yaml b/demos/operator/demo-obj.yaml
deleted file mode 100644
index 7957912..0000000
--- a/demos/operator/demo-obj.yaml
+++ /dev/null
@@ -1,26 +0,0 @@
-# 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.
-#
-# A demo custom resource for the Kopf example operators.
----
-apiVersion: nuvolaris.org/v1
-kind: Sample
-metadata:
- name: obj
- namespace: demo
-spec:
- count: 2
diff --git a/demos/operator/demo1.py b/demos/operator/demo1.py
deleted file mode 100644
index b2b7345..0000000
--- a/demos/operator/demo1.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# 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 kopf
-import os, os.path
-import logging
-
-@kopf.on.login()
-def sample_login(**kwargs):
- token = '/var/run/secrets/kubernetes.io/serviceaccount/token'
- if os.path.isfile(token):
- logging.debug("found serviceaccount token: login via pykube in kubernetes")
- return kopf.login_with_service_account(**kwargs)
- logging.debug("login via client")
- return kopf.login_with_kubeconfig(**kwargs)
-
-@kopf.on.create('nuvolaris.org', 'v1', 'samples')
-def sample_create(spec, **kwargs):
- print(spec)
- return { "message": "created" }
-
-@kopf.on.delete('nuvolaris.org', 'v1', 'samples')
-def sample_delete(spec, **kwargs):
- print(spec)
- return { "message": "delete" }
diff --git a/demos/operator/demo2.py b/demos/operator/demo2.py
deleted file mode 100644
index 9964d26..0000000
--- a/demos/operator/demo2.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# 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 kopf
-import os, os.path
-import subprocess
-import logging
-
-def patch(n):
- return f"""apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: demo-deploy
- namespace: demo
-spec:
- replicas: {n}
-"""
-
-def kubectl(cmd, patch):
- print(f"kubectl {cmd}")
- with open(f"deploy/patch.yaml", "w") as f:
- f.write(patch)
- res = subprocess.run(["kubectl", cmd, "-k", "deploy"], capture_output=True)
- if res.returncode == 0:
- return res.stdout.decode()
- return res.stderr.decode()
-
-@kopf.on.login()
-def sample_login(**kwargs):
- token = '/var/run/secrets/kubernetes.io/serviceaccount/token'
- if os.path.isfile(token):
- logging.debug("found serviceaccount token: login via pykube in kubernetes")
- return kopf.login_with_service_account(**kwargs)
- logging.debug("login via client")
- return kopf.login_with_kubeconfig(**kwargs)
-
-@kopf.on.create('nuvolaris.org', 'v1', 'samples')
-def sample_create(spec, **kwargs):
- count = spec["count"]
- message = kubectl("apply", patch(count))
- return { "message": message }
-
-@kopf.on.delete('nuvolaris.org', 'v1', 'samples')
-def sample_delete(spec, **kwargs):
- count = spec["count"]
- message = kubectl("delete", patch(count))
- return { "message": "delete" }
diff --git a/demos/operator/demomock.py b/demos/operator/demomock.py
deleted file mode 100644
index 12df9d4..0000000
--- a/demos/operator/demomock.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# 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.
-#
-from unittest.mock import MagicMock
-from unittest.mock import patch
-from unittest.mock import create_autospec
-
-kfn = create_autospec(kube.kubectl, return_value="ok")
-p = patch('nuvolaris.kube.kubectl', kfn)
-p.start()
-openwhisk.create()
-kube.kubectl("boh")
-p.stop()
\ No newline at end of file
diff --git a/demos/operator/obj.yaml b/demos/operator/obj.yaml
deleted file mode 100644
index 146f655..0000000
--- a/demos/operator/obj.yaml
+++ /dev/null
@@ -1,32 +0,0 @@
-# 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.
-#
-# A demo custom resource for the Kopf example operators.
-apiVersion: kopf.dev/v1
-kind: KopfExample
-metadata:
- name: kopf-example-1
- labels:
- somelabel: somevalue
- annotations:
- someannotation: somevalue
-spec:
- duration: 1m
- field: value
- items:
- - item1
- - item2
diff --git a/demos/operator/patch.yaml b/demos/operator/patch.yaml
deleted file mode 100644
index 13ec133..0000000
--- a/demos/operator/patch.yaml
+++ /dev/null
@@ -1,24 +0,0 @@
-# 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.
-#
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: demo-deploy
- namespace: demo
-spec:
- replicas: 1
diff --git a/demos/operator/poetry.lock b/demos/operator/poetry.lock
deleted file mode 100644
index 85a6e6a..0000000
--- a/demos/operator/poetry.lock
+++ /dev/null
@@ -1,1179 +0,0 @@
-# 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.
-#
-[[package]]
-name = "aiohttp"
-version = "3.8.1"
-description = "Async http client/server framework (asyncio)"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-aiosignal = ">=1.1.2"
-async-timeout = ">=4.0.0a3,<5.0"
-attrs = ">=17.3.0"
-charset-normalizer = ">=2.0,<3.0"
-frozenlist = ">=1.1.1"
-multidict = ">=4.5,<7.0"
-yarl = ">=1.0,<2.0"
-
-[package.extras]
-speedups = ["aiodns", "brotli", "cchardet"]
-
-[[package]]
-name = "aiosignal"
-version = "1.2.0"
-description = "aiosignal: a list of registered asynchronous callbacks"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-frozenlist = ">=1.1.0"
-
-[[package]]
-name = "appnope"
-version = "0.1.2"
-description = "Disable App Nap on macOS >= 10.9"
-category = "dev"
-optional = false
-python-versions = "*"
-
-[[package]]
-name = "async-timeout"
-version = "4.0.2"
-description = "Timeout context manager for asyncio programs"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[[package]]
-name = "attrs"
-version = "21.4.0"
-description = "Classes Without Boilerplate"
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-
-[package.extras]
-dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"]
-docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
-tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"]
-tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"]
-
-[[package]]
-name = "backcall"
-version = "0.2.0"
-description = "Specifications for callback functions passed in to an API"
-category = "dev"
-optional = false
-python-versions = "*"
-
-[[package]]
-name = "cachetools"
-version = "4.2.4"
-description = "Extensible memoizing collections and decorators"
-category = "main"
-optional = false
-python-versions = "~=3.5"
-
-[[package]]
-name = "certifi"
-version = "2021.10.8"
-description = "Python package for providing Mozilla's CA Bundle."
-category = "main"
-optional = false
-python-versions = "*"
-
-[[package]]
-name = "charset-normalizer"
-version = "2.0.10"
-description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
-category = "main"
-optional = false
-python-versions = ">=3.5.0"
-
-[package.extras]
-unicode_backport = ["unicodedata2"]
-
-[[package]]
-name = "click"
-version = "8.0.3"
-description = "Composable command line interface toolkit"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-colorama = {version = "*", markers = "platform_system == \"Windows\""}
-
-[[package]]
-name = "colorama"
-version = "0.4.4"
-description = "Cross-platform colored terminal text."
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-
-[[package]]
-name = "decorator"
-version = "5.1.1"
-description = "Decorators for Humans"
-category = "dev"
-optional = false
-python-versions = ">=3.5"
-
-[[package]]
-name = "frozenlist"
-version = "1.2.0"
-description = "A list-like structure which implements collections.abc.MutableSequence"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[[package]]
-name = "google-auth"
-version = "2.3.3"
-description = "Google Authentication Library"
-category = "main"
-optional = false
-python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*"
-
-[package.dependencies]
-cachetools = ">=2.0.0,<5.0"
-pyasn1-modules = ">=0.2.1"
-rsa = {version = ">=3.1.4,<5", markers = "python_version >= \"3.6\""}
-six = ">=1.9.0"
-
-[package.extras]
-aiohttp = ["requests (>=2.20.0,<3.0.0dev)", "aiohttp (>=3.6.2,<4.0.0dev)"]
-pyopenssl = ["pyopenssl (>=20.0.0)"]
-reauth = ["pyu2f (>=0.1.5)"]
-
-[[package]]
-name = "httplib2"
-version = "0.20.2"
-description = "A comprehensive HTTP client library."
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-
-[package.dependencies]
-pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0.2,<3.0.3 || >3.0.3,<4", markers = "python_version > \"3.0\""}
-
-[[package]]
-name = "idna"
-version = "3.3"
-description = "Internationalized Domain Names in Applications (IDNA)"
-category = "main"
-optional = false
-python-versions = ">=3.5"
-
-[[package]]
-name = "ipython"
-version = "7.31.0"
-description = "IPython: Productive Interactive Computing"
-category = "dev"
-optional = false
-python-versions = ">=3.7"
-
-[package.dependencies]
-appnope = {version = "*", markers = "sys_platform == \"darwin\""}
-backcall = "*"
-colorama = {version = "*", markers = "sys_platform == \"win32\""}
-decorator = "*"
-jedi = ">=0.16"
-matplotlib-inline = "*"
-pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""}
-pickleshare = "*"
-prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0"
-pygments = "*"
-traitlets = ">=4.2"
-
-[package.extras]
-all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.17)", "pygments", "qtconsole", "requests", "testpath"]
-doc = ["Sphinx (>=1.3)"]
-kernel = ["ipykernel"]
-nbconvert = ["nbconvert"]
-nbformat = ["nbformat"]
-notebook = ["notebook", "ipywidgets"]
-parallel = ["ipyparallel"]
-qtconsole = ["qtconsole"]
-test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.17)"]
-
-[[package]]
-name = "iso8601"
-version = "1.0.2"
-description = "Simple module to parse ISO 8601 dates"
-category = "main"
-optional = false
-python-versions = ">=3.6.2,<4.0"
-
-[[package]]
-name = "jedi"
-version = "0.18.1"
-description = "An autocompletion tool for Python that can be used for text editors."
-category = "dev"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-parso = ">=0.8.0,<0.9.0"
-
-[package.extras]
-qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
-testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<7.0.0)"]
-
-[[package]]
-name = "kopf"
-version = "1.35.3"
-description = "Kubernetes Operator Pythonic Framework (Kopf)"
-category = "main"
-optional = false
-python-versions = ">=3.7"
-
-[package.dependencies]
-aiohttp = "<4.0.0"
-click = "*"
-iso8601 = "*"
-kubernetes = {version = "*", optional = true, markers = "extra == \"full-auth\""}
-pykube-ng = {version = "*", optional = true, markers = "extra == \"full-auth\""}
-python-json-logger = "*"
-pyyaml = "*"
-typing-extensions = "*"
-
-[package.extras]
-dev = ["pyngrok", "oscrypto", "certbuilder", "certvalidator"]
-full-auth = ["pykube-ng", "kubernetes"]
-
-[[package]]
-name = "kubernetes"
-version = "21.7.0"
-description = "Kubernetes python client"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-certifi = ">=14.05.14"
-google-auth = ">=1.0.1"
-python-dateutil = ">=2.5.3"
-pyyaml = ">=5.4.1"
-requests = "*"
-requests-oauthlib = "*"
-six = ">=1.9.0"
-urllib3 = ">=1.24.2"
-websocket-client = ">=0.32.0,<0.40.0 || >0.40.0,<0.41.0 || >=0.43.0"
-
-[package.extras]
-adal = ["adal (>=1.0.2)"]
-
-[[package]]
-name = "matplotlib-inline"
-version = "0.1.3"
-description = "Inline Matplotlib backend for Jupyter"
-category = "dev"
-optional = false
-python-versions = ">=3.5"
-
-[package.dependencies]
-traitlets = "*"
-
-[[package]]
-name = "multidict"
-version = "5.2.0"
-description = "multidict implementation"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[[package]]
-name = "oauth2client"
-version = "4.1.3"
-description = "OAuth 2.0 client library"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-httplib2 = ">=0.9.1"
-pyasn1 = ">=0.1.7"
-pyasn1-modules = ">=0.0.5"
-rsa = ">=3.1.4"
-six = ">=1.6.1"
-
-[[package]]
-name = "oauthlib"
-version = "3.1.1"
-description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.extras]
-rsa = ["cryptography (>=3.0.0,<4)"]
-signals = ["blinker (>=1.4.0)"]
-signedtoken = ["cryptography (>=3.0.0,<4)", "pyjwt (>=2.0.0,<3)"]
-
-[[package]]
-name = "parso"
-version = "0.8.3"
-description = "A Python Parser"
-category = "dev"
-optional = false
-python-versions = ">=3.6"
-
-[package.extras]
-qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
-testing = ["docopt", "pytest (<6.0.0)"]
-
-[[package]]
-name = "pexpect"
-version = "4.8.0"
-description = "Pexpect allows easy control of interactive console applications."
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-ptyprocess = ">=0.5"
-
-[[package]]
-name = "pickleshare"
-version = "0.7.5"
-description = "Tiny 'shelve'-like database with concurrency support"
-category = "dev"
-optional = false
-python-versions = "*"
-
-[[package]]
-name = "prompt-toolkit"
-version = "3.0.24"
-description = "Library for building powerful interactive command lines in Python"
-category = "dev"
-optional = false
-python-versions = ">=3.6.2"
-
-[package.dependencies]
-wcwidth = "*"
-
-[[package]]
-name = "ptyprocess"
-version = "0.7.0"
-description = "Run a subprocess in a pseudo terminal"
-category = "dev"
-optional = false
-python-versions = "*"
-
-[[package]]
-name = "pyasn1"
-version = "0.4.8"
-description = "ASN.1 types and codecs"
-category = "main"
-optional = false
-python-versions = "*"
-
-[[package]]
-name = "pyasn1-modules"
-version = "0.2.8"
-description = "A collection of ASN.1-based protocols modules."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-pyasn1 = ">=0.4.6,<0.5.0"
-
-[[package]]
-name = "pygments"
-version = "2.11.2"
-description = "Pygments is a syntax highlighting package written in Python."
-category = "dev"
-optional = false
-python-versions = ">=3.5"
-
-[[package]]
-name = "pykube"
-version = "0.15.0"
-description = "Python client library for Kubernetes"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-oauth2client = "*"
-PyYAML = "*"
-requests = ">=2.12"
-requests-oauthlib = "*"
-six = ">=1.10.0"
-tzlocal = "*"
-
-[[package]]
-name = "pykube-ng"
-version = "21.10.0"
-description = "Python client library for Kubernetes"
-category = "main"
-optional = false
-python-versions = ">=3.8"
-
-[package.dependencies]
-pyyaml = "*"
-requests = ">=2.12"
-
-[package.extras]
-gcp = ["google-auth", "jsonpath-ng"]
-oidc = ["requests-oauthlib (>=1.3.0,<2.0.0)"]
-
-[[package]]
-name = "pyparsing"
-version = "3.0.6"
-description = "Python parsing module"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.extras]
-diagrams = ["jinja2", "railroad-diagrams"]
-
-[[package]]
-name = "python-dateutil"
-version = "2.8.2"
-description = "Extensions to the standard Python datetime module"
-category = "main"
-optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
-
-[package.dependencies]
-six = ">=1.5"
-
-[[package]]
-name = "python-json-logger"
-version = "2.0.2"
-description = "A python library adding a json log formatter"
-category = "main"
-optional = false
-python-versions = ">=3.5"
-
-[[package]]
-name = "pytz-deprecation-shim"
-version = "0.1.0.post0"
-description = "Shims to make deprecation of pytz easier"
-category = "main"
-optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
-
-[package.dependencies]
-tzdata = {version = "*", markers = "python_version >= \"3.6\""}
-
-[[package]]
-name = "pyyaml"
-version = "6.0"
-description = "YAML parser and emitter for Python"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[[package]]
-name = "requests"
-version = "2.27.1"
-description = "Python HTTP for Humans."
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
-
-[package.dependencies]
-certifi = ">=2017.4.17"
-charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""}
-idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""}
-urllib3 = ">=1.21.1,<1.27"
-
-[package.extras]
-socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
-use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"]
-
-[[package]]
-name = "requests-oauthlib"
-version = "1.3.0"
-description = "OAuthlib authentication support for Requests."
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-
-[package.dependencies]
-oauthlib = ">=3.0.0"
-requests = ">=2.0.0"
-
-[package.extras]
-rsa = ["oauthlib[signedtoken] (>=3.0.0)"]
-
-[[package]]
-name = "rsa"
-version = "4.8"
-description = "Pure-Python RSA implementation"
-category = "main"
-optional = false
-python-versions = ">=3.6,<4"
-
-[package.dependencies]
-pyasn1 = ">=0.1.3"
-
-[[package]]
-name = "six"
-version = "1.16.0"
-description = "Python 2 and 3 compatibility utilities"
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
-
-[[package]]
-name = "traitlets"
-version = "5.1.1"
-description = "Traitlets Python configuration system"
-category = "dev"
-optional = false
-python-versions = ">=3.7"
-
-[package.extras]
-test = ["pytest"]
-
-[[package]]
-name = "typing-extensions"
-version = "4.0.1"
-description = "Backported and Experimental Type Hints for Python 3.6+"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[[package]]
-name = "tzdata"
-version = "2021.5"
-description = "Provider of IANA time zone data"
-category = "main"
-optional = false
-python-versions = ">=2"
-
-[[package]]
-name = "tzlocal"
-version = "4.1"
-description = "tzinfo object for the local timezone"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-pytz-deprecation-shim = "*"
-tzdata = {version = "*", markers = "platform_system == \"Windows\""}
-
-[package.extras]
-devenv = ["black", "pyroma", "pytest-cov", "zest.releaser"]
-test = ["pytest-mock (>=3.3)", "pytest (>=4.3)"]
-
-[[package]]
-name = "urllib3"
-version = "1.26.8"
-description = "HTTP library with thread-safe connection pooling, file post, and more."
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
-
-[package.extras]
-brotli = ["brotlipy (>=0.6.0)"]
-secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
-socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
-
-[[package]]
-name = "wcwidth"
-version = "0.2.5"
-description = "Measures the displayed width of unicode strings in a terminal"
-category = "dev"
-optional = false
-python-versions = "*"
-
-[[package]]
-name = "websocket-client"
-version = "1.2.3"
-description = "WebSocket client for Python with low level API options"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.extras]
-docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"]
-optional = ["python-socks", "wsaccel"]
-test = ["websockets"]
-
-[[package]]
-name = "yarl"
-version = "1.7.2"
-description = "Yet another URL library"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-idna = ">=2.0"
-multidict = ">=4.0"
-
-[metadata]
-lock-version = "1.1"
-python-versions = "^3.9"
-content-hash = "9cd584aca7161fdbf1bfbf330e08d90db13e0d1695c5bd394f33ad7fbb059115"
-
-[metadata.files]
-aiohttp = [
- {file = "aiohttp-3.8.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1ed0b6477896559f17b9eaeb6d38e07f7f9ffe40b9f0f9627ae8b9926ae260a8"},
- {file = "aiohttp-3.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7dadf3c307b31e0e61689cbf9e06be7a867c563d5a63ce9dca578f956609abf8"},
- {file = "aiohttp-3.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a79004bb58748f31ae1cbe9fa891054baaa46fb106c2dc7af9f8e3304dc30316"},
- {file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12de6add4038df8f72fac606dff775791a60f113a725c960f2bab01d8b8e6b15"},
- {file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f0d5f33feb5f69ddd57a4a4bd3d56c719a141080b445cbf18f238973c5c9923"},
- {file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eaba923151d9deea315be1f3e2b31cc39a6d1d2f682f942905951f4e40200922"},
- {file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:099ebd2c37ac74cce10a3527d2b49af80243e2a4fa39e7bce41617fbc35fa3c1"},
- {file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2e5d962cf7e1d426aa0e528a7e198658cdc8aa4fe87f781d039ad75dcd52c516"},
- {file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fa0ffcace9b3aa34d205d8130f7873fcfefcb6a4dd3dd705b0dab69af6712642"},
- {file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:61bfc23df345d8c9716d03717c2ed5e27374e0fe6f659ea64edcd27b4b044cf7"},
- {file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:31560d268ff62143e92423ef183680b9829b1b482c011713ae941997921eebc8"},
- {file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:01d7bdb774a9acc838e6b8f1d114f45303841b89b95984cbb7d80ea41172a9e3"},
- {file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:97ef77eb6b044134c0b3a96e16abcb05ecce892965a2124c566af0fd60f717e2"},
- {file = "aiohttp-3.8.1-cp310-cp310-win32.whl", hash = "sha256:c2aef4703f1f2ddc6df17519885dbfa3514929149d3ff900b73f45998f2532fa"},
- {file = "aiohttp-3.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:713ac174a629d39b7c6a3aa757b337599798da4c1157114a314e4e391cd28e32"},
- {file = "aiohttp-3.8.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:473d93d4450880fe278696549f2e7aed8cd23708c3c1997981464475f32137db"},
- {file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b5eeae8e019e7aad8af8bb314fb908dd2e028b3cdaad87ec05095394cce632"},
- {file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3af642b43ce56c24d063325dd2cf20ee012d2b9ba4c3c008755a301aaea720ad"},
- {file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3630c3ef435c0a7c549ba170a0633a56e92629aeed0e707fec832dee313fb7a"},
- {file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4a4a4e30bf1edcad13fb0804300557aedd07a92cabc74382fdd0ba6ca2661091"},
- {file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6f8b01295e26c68b3a1b90efb7a89029110d3a4139270b24fda961893216c440"},
- {file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a25fa703a527158aaf10dafd956f7d42ac6d30ec80e9a70846253dd13e2f067b"},
- {file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5bfde62d1d2641a1f5173b8c8c2d96ceb4854f54a44c23102e2ccc7e02f003ec"},
- {file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:51467000f3647d519272392f484126aa716f747859794ac9924a7aafa86cd411"},
- {file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:03a6d5349c9ee8f79ab3ff3694d6ce1cfc3ced1c9d36200cb8f08ba06bd3b782"},
- {file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:102e487eeb82afac440581e5d7f8f44560b36cf0bdd11abc51a46c1cd88914d4"},
- {file = "aiohttp-3.8.1-cp36-cp36m-win32.whl", hash = "sha256:4aed991a28ea3ce320dc8ce655875e1e00a11bdd29fe9444dd4f88c30d558602"},
- {file = "aiohttp-3.8.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b0e20cddbd676ab8a64c774fefa0ad787cc506afd844de95da56060348021e96"},
- {file = "aiohttp-3.8.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:37951ad2f4a6df6506750a23f7cbabad24c73c65f23f72e95897bb2cecbae676"},
- {file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c23b1ad869653bc818e972b7a3a79852d0e494e9ab7e1a701a3decc49c20d51"},
- {file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:15b09b06dae900777833fe7fc4b4aa426556ce95847a3e8d7548e2d19e34edb8"},
- {file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:477c3ea0ba410b2b56b7efb072c36fa91b1e6fc331761798fa3f28bb224830dd"},
- {file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2f2f69dca064926e79997f45b2f34e202b320fd3782f17a91941f7eb85502ee2"},
- {file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ef9612483cb35171d51d9173647eed5d0069eaa2ee812793a75373447d487aa4"},
- {file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6d69f36d445c45cda7b3b26afef2fc34ef5ac0cdc75584a87ef307ee3c8c6d00"},
- {file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:55c3d1072704d27401c92339144d199d9de7b52627f724a949fc7d5fc56d8b93"},
- {file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b9d00268fcb9f66fbcc7cd9fe423741d90c75ee029a1d15c09b22d23253c0a44"},
- {file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:07b05cd3305e8a73112103c834e91cd27ce5b4bd07850c4b4dbd1877d3f45be7"},
- {file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c34dc4958b232ef6188c4318cb7b2c2d80521c9a56c52449f8f93ab7bc2a8a1c"},
- {file = "aiohttp-3.8.1-cp37-cp37m-win32.whl", hash = "sha256:d2f9b69293c33aaa53d923032fe227feac867f81682f002ce33ffae978f0a9a9"},
- {file = "aiohttp-3.8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:6ae828d3a003f03ae31915c31fa684b9890ea44c9c989056fea96e3d12a9fa17"},
- {file = "aiohttp-3.8.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0c7ebbbde809ff4e970824b2b6cb7e4222be6b95a296e46c03cf050878fc1785"},
- {file = "aiohttp-3.8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8b7ef7cbd4fec9a1e811a5de813311ed4f7ac7d93e0fda233c9b3e1428f7dd7b"},
- {file = "aiohttp-3.8.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c3d6a4d0619e09dcd61021debf7059955c2004fa29f48788a3dfaf9c9901a7cd"},
- {file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:718626a174e7e467f0558954f94af117b7d4695d48eb980146016afa4b580b2e"},
- {file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:589c72667a5febd36f1315aa6e5f56dd4aa4862df295cb51c769d16142ddd7cd"},
- {file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2ed076098b171573161eb146afcb9129b5ff63308960aeca4b676d9d3c35e700"},
- {file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:086f92daf51a032d062ec5f58af5ca6a44d082c35299c96376a41cbb33034675"},
- {file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:11691cf4dc5b94236ccc609b70fec991234e7ef8d4c02dd0c9668d1e486f5abf"},
- {file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:31d1e1c0dbf19ebccbfd62eff461518dcb1e307b195e93bba60c965a4dcf1ba0"},
- {file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:11a67c0d562e07067c4e86bffc1553f2cf5b664d6111c894671b2b8712f3aba5"},
- {file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:bb01ba6b0d3f6c68b89fce7305080145d4877ad3acaed424bae4d4ee75faa950"},
- {file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:44db35a9e15d6fe5c40d74952e803b1d96e964f683b5a78c3cc64eb177878155"},
- {file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:844a9b460871ee0a0b0b68a64890dae9c415e513db0f4a7e3cab41a0f2fedf33"},
- {file = "aiohttp-3.8.1-cp38-cp38-win32.whl", hash = "sha256:7d08744e9bae2ca9c382581f7dce1273fe3c9bae94ff572c3626e8da5b193c6a"},
- {file = "aiohttp-3.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:04d48b8ce6ab3cf2097b1855e1505181bdd05586ca275f2505514a6e274e8e75"},
- {file = "aiohttp-3.8.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f5315a2eb0239185af1bddb1abf472d877fede3cc8d143c6cddad37678293237"},
- {file = "aiohttp-3.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a996d01ca39b8dfe77440f3cd600825d05841088fd6bc0144cc6c2ec14cc5f74"},
- {file = "aiohttp-3.8.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:13487abd2f761d4be7c8ff9080de2671e53fff69711d46de703c310c4c9317ca"},
- {file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea302f34477fda3f85560a06d9ebdc7fa41e82420e892fc50b577e35fc6a50b2"},
- {file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2f635ce61a89c5732537a7896b6319a8fcfa23ba09bec36e1b1ac0ab31270d2"},
- {file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e999f2d0e12eea01caeecb17b653f3713d758f6dcc770417cf29ef08d3931421"},
- {file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0770e2806a30e744b4e21c9d73b7bee18a1cfa3c47991ee2e5a65b887c49d5cf"},
- {file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d15367ce87c8e9e09b0f989bfd72dc641bcd04ba091c68cd305312d00962addd"},
- {file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6c7cefb4b0640703eb1069835c02486669312bf2f12b48a748e0a7756d0de33d"},
- {file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:71927042ed6365a09a98a6377501af5c9f0a4d38083652bcd2281a06a5976724"},
- {file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:28d490af82bc6b7ce53ff31337a18a10498303fe66f701ab65ef27e143c3b0ef"},
- {file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:b6613280ccedf24354406caf785db748bebbddcf31408b20c0b48cb86af76866"},
- {file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:81e3d8c34c623ca4e36c46524a3530e99c0bc95ed068fd6e9b55cb721d408fb2"},
- {file = "aiohttp-3.8.1-cp39-cp39-win32.whl", hash = "sha256:7187a76598bdb895af0adbd2fb7474d7f6025d170bc0a1130242da817ce9e7d1"},
- {file = "aiohttp-3.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:1c182cb873bc91b411e184dab7a2b664d4fea2743df0e4d57402f7f3fa644bac"},
- {file = "aiohttp-3.8.1.tar.gz", hash = "sha256:fc5471e1a54de15ef71c1bc6ebe80d4dc681ea600e68bfd1cbce40427f0b7578"},
-]
-aiosignal = [
- {file = "aiosignal-1.2.0-py3-none-any.whl", hash = "sha256:26e62109036cd181df6e6ad646f91f0dcfd05fe16d0cb924138ff2ab75d64e3a"},
- {file = "aiosignal-1.2.0.tar.gz", hash = "sha256:78ed67db6c7b7ced4f98e495e572106d5c432a93e1ddd1bf475e1dc05f5b7df2"},
-]
-appnope = [
- {file = "appnope-0.1.2-py2.py3-none-any.whl", hash = "sha256:93aa393e9d6c54c5cd570ccadd8edad61ea0c4b9ea7a01409020c9aa019eb442"},
- {file = "appnope-0.1.2.tar.gz", hash = "sha256:dd83cd4b5b460958838f6eb3000c660b1f9caf2a5b1de4264e941512f603258a"},
-]
-async-timeout = [
- {file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"},
- {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"},
-]
-attrs = [
- {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
- {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
-]
-backcall = [
- {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"},
- {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"},
-]
-cachetools = [
- {file = "cachetools-4.2.4-py3-none-any.whl", hash = "sha256:92971d3cb7d2a97efff7c7bb1657f21a8f5fb309a37530537c71b1774189f2d1"},
- {file = "cachetools-4.2.4.tar.gz", hash = "sha256:89ea6f1b638d5a73a4f9226be57ac5e4f399d22770b92355f92dcb0f7f001693"},
-]
-certifi = [
- {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"},
- {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"},
-]
-charset-normalizer = [
- {file = "charset-normalizer-2.0.10.tar.gz", hash = "sha256:876d180e9d7432c5d1dfd4c5d26b72f099d503e8fcc0feb7532c9289be60fcbd"},
- {file = "charset_normalizer-2.0.10-py3-none-any.whl", hash = "sha256:cb957888737fc0bbcd78e3df769addb41fd1ff8cf950dc9e7ad7793f1bf44455"},
-]
-click = [
- {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"},
- {file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"},
-]
-colorama = [
- {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
- {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
-]
-decorator = [
- {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"},
- {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
-]
-frozenlist = [
- {file = "frozenlist-1.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:977a1438d0e0d96573fd679d291a1542097ea9f4918a8b6494b06610dfeefbf9"},
- {file = "frozenlist-1.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a8d86547a5e98d9edd47c432f7a14b0c5592624b496ae9880fb6332f34af1edc"},
- {file = "frozenlist-1.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:181754275d5d32487431a0a29add4f897968b7157204bc1eaaf0a0ce80c5ba7d"},
- {file = "frozenlist-1.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5df31bb2b974f379d230a25943d9bf0d3bc666b4b0807394b131a28fca2b0e5f"},
- {file = "frozenlist-1.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4766632cd8a68e4f10f156a12c9acd7b1609941525569dd3636d859d79279ed3"},
- {file = "frozenlist-1.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16eef427c51cb1203a7c0ab59d1b8abccaba9a4f58c4bfca6ed278fc896dc193"},
- {file = "frozenlist-1.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:01d79515ed5aa3d699b05f6bdcf1fe9087d61d6b53882aa599a10853f0479c6c"},
- {file = "frozenlist-1.2.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:28e164722ea0df0cf6d48c4d5bdf3d19e87aaa6dfb39b0ba91153f224b912020"},
- {file = "frozenlist-1.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e63ad0beef6ece06475d29f47d1f2f29727805376e09850ebf64f90777962792"},
- {file = "frozenlist-1.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:41de4db9b9501679cf7cddc16d07ac0f10ef7eb58c525a1c8cbff43022bddca4"},
- {file = "frozenlist-1.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c6a9d84ee6427b65a81fc24e6ef589cb794009f5ca4150151251c062773e7ed2"},
- {file = "frozenlist-1.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:f5f3b2942c3b8b9bfe76b408bbaba3d3bb305ee3693e8b1d631fe0a0d4f93673"},
- {file = "frozenlist-1.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c98d3c04701773ad60d9545cd96df94d955329efc7743fdb96422c4b669c633b"},
- {file = "frozenlist-1.2.0-cp310-cp310-win32.whl", hash = "sha256:72cfbeab7a920ea9e74b19aa0afe3b4ad9c89471e3badc985d08756efa9b813b"},
- {file = "frozenlist-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:11ff401951b5ac8c0701a804f503d72c048173208490c54ebb8d7bb7c07a6d00"},
- {file = "frozenlist-1.2.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b46f997d5ed6d222a863b02cdc9c299101ee27974d9bbb2fd1b3c8441311c408"},
- {file = "frozenlist-1.2.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:351686ca020d1bcd238596b1fa5c8efcbc21bffda9d0efe237aaa60348421e2a"},
- {file = "frozenlist-1.2.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfbaa08cf1452acad9cb1c1d7b89394a41e712f88df522cea1a0f296b57782a0"},
- {file = "frozenlist-1.2.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2ae2f5e9fa10805fb1c9adbfefaaecedd9e31849434be462c3960a0139ed729"},
- {file = "frozenlist-1.2.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6790b8d96bbb74b7a6f4594b6f131bd23056c25f2aa5d816bd177d95245a30e3"},
- {file = "frozenlist-1.2.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:41f62468af1bd4e4b42b5508a3fe8cc46a693f0cdd0ca2f443f51f207893d837"},
- {file = "frozenlist-1.2.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:ec6cf345771cdb00791d271af9a0a6fbfc2b6dd44cb753f1eeaa256e21622adb"},
- {file = "frozenlist-1.2.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:14a5cef795ae3e28fb504b73e797c1800e9249f950e1c964bb6bdc8d77871161"},
- {file = "frozenlist-1.2.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:8b54cdd2fda15467b9b0bfa78cee2ddf6dbb4585ef23a16e14926f4b076dfae4"},
- {file = "frozenlist-1.2.0-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:f025f1d6825725b09c0038775acab9ae94264453a696cc797ce20c0769a7b367"},
- {file = "frozenlist-1.2.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:84e97f59211b5b9083a2e7a45abf91cfb441369e8bb6d1f5287382c1c526def3"},
- {file = "frozenlist-1.2.0-cp36-cp36m-win32.whl", hash = "sha256:c5328ed53fdb0a73c8a50105306a3bc013e5ca36cca714ec4f7bd31d38d8a97f"},
- {file = "frozenlist-1.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:9ade70aea559ca98f4b1b1e5650c45678052e76a8ab2f76d90f2ac64180215a2"},
- {file = "frozenlist-1.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0d3ffa8772464441b52489b985d46001e2853a3b082c655ec5fad9fb6a3d618"},
- {file = "frozenlist-1.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3457f8cf86deb6ce1ba67e120f1b0128fcba1332a180722756597253c465fc1d"},
- {file = "frozenlist-1.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a72eecf37eface331636951249d878750db84034927c997d47f7f78a573b72b"},
- {file = "frozenlist-1.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:acc4614e8d1feb9f46dd829a8e771b8f5c4b1051365d02efb27a3229048ade8a"},
- {file = "frozenlist-1.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:87521e32e18a2223311afc2492ef2d99946337da0779ddcda77b82ee7319df59"},
- {file = "frozenlist-1.2.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8b4c7665a17c3a5430edb663e4ad4e1ad457614d1b2f2b7f87052e2ef4fa45ca"},
- {file = "frozenlist-1.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ed58803563a8c87cf4c0771366cf0ad1aa265b6b0ae54cbbb53013480c7ad74d"},
- {file = "frozenlist-1.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:aa44c4740b4e23fcfa259e9dd52315d2b1770064cde9507457e4c4a65a04c397"},
- {file = "frozenlist-1.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:2de5b931701257d50771a032bba4e448ff958076380b049fd36ed8738fdb375b"},
- {file = "frozenlist-1.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:6e105013fa84623c057a4381dc8ea0361f4d682c11f3816cc80f49a1f3bc17c6"},
- {file = "frozenlist-1.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:705c184b77565955a99dc360f359e8249580c6b7eaa4dc0227caa861ef46b27a"},
- {file = "frozenlist-1.2.0-cp37-cp37m-win32.whl", hash = "sha256:a37594ad6356e50073fe4f60aa4187b97d15329f2138124d252a5a19c8553ea4"},
- {file = "frozenlist-1.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:25b358aaa7dba5891b05968dd539f5856d69f522b6de0bf34e61f133e077c1a4"},
- {file = "frozenlist-1.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af2a51c8a381d76eabb76f228f565ed4c3701441ecec101dd18be70ebd483cfd"},
- {file = "frozenlist-1.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:82d22f6e6f2916e837c91c860140ef9947e31194c82aaeda843d6551cec92f19"},
- {file = "frozenlist-1.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1cfe6fef507f8bac40f009c85c7eddfed88c1c0d38c75e72fe10476cef94e10f"},
- {file = "frozenlist-1.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26f602e380a5132880fa245c92030abb0fc6ff34e0c5500600366cedc6adb06a"},
- {file = "frozenlist-1.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ad065b2ebd09f32511ff2be35c5dfafee6192978b5a1e9d279a5c6e121e3b03"},
- {file = "frozenlist-1.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bc93f5f62df3bdc1f677066327fc81f92b83644852a31c6aa9b32c2dde86ea7d"},
- {file = "frozenlist-1.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:89fdfc84c6bf0bff2ff3170bb34ecba8a6911b260d318d377171429c4be18c73"},
- {file = "frozenlist-1.2.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:47b2848e464883d0bbdcd9493c67443e5e695a84694efff0476f9059b4cb6257"},
- {file = "frozenlist-1.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4f52d0732e56906f8ddea4bd856192984650282424049c956857fed43697ea43"},
- {file = "frozenlist-1.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:16ef7dd5b7d17495404a2e7a49bac1bc13d6d20c16d11f4133c757dd94c4144c"},
- {file = "frozenlist-1.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:1cf63243bc5f5c19762943b0aa9e0d3fb3723d0c514d820a18a9b9a5ef864315"},
- {file = "frozenlist-1.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:54a1e09ab7a69f843cd28fefd2bcaf23edb9e3a8d7680032c8968b8ac934587d"},
- {file = "frozenlist-1.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:954b154a4533ef28bd3e83ffdf4eadf39deeda9e38fb8feaf066d6069885e034"},
- {file = "frozenlist-1.2.0-cp38-cp38-win32.whl", hash = "sha256:cb3957c39668d10e2b486acc85f94153520a23263b6401e8f59422ef65b9520d"},
- {file = "frozenlist-1.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:0a7c7cce70e41bc13d7d50f0e5dd175f14a4f1837a8549b0936ed0cbe6170bf9"},
- {file = "frozenlist-1.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4c457220468d734e3077580a3642b7f682f5fd9507f17ddf1029452450912cdc"},
- {file = "frozenlist-1.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e74f8b4d8677ebb4015ac01fcaf05f34e8a1f22775db1f304f497f2f88fdc697"},
- {file = "frozenlist-1.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fbd4844ff111449f3bbe20ba24fbb906b5b1c2384d0f3287c9f7da2354ce6d23"},
- {file = "frozenlist-1.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0081a623c886197ff8de9e635528fd7e6a387dccef432149e25c13946cb0cd0"},
- {file = "frozenlist-1.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9b6e21e5770df2dea06cb7b6323fbc008b13c4a4e3b52cb54685276479ee7676"},
- {file = "frozenlist-1.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:406aeb340613b4b559db78d86864485f68919b7141dec82aba24d1477fd2976f"},
- {file = "frozenlist-1.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:878ebe074839d649a1cdb03a61077d05760624f36d196884a5cafb12290e187b"},
- {file = "frozenlist-1.2.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1fef737fd1388f9b93bba8808c5f63058113c10f4e3c0763ced68431773f72f9"},
- {file = "frozenlist-1.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4a495c3d513573b0b3f935bfa887a85d9ae09f0627cf47cad17d0cc9b9ba5c38"},
- {file = "frozenlist-1.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e7d0dd3e727c70c2680f5f09a0775525229809f1a35d8552b92ff10b2b14f2c2"},
- {file = "frozenlist-1.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:66a518731a21a55b7d3e087b430f1956a36793acc15912e2878431c7aec54210"},
- {file = "frozenlist-1.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:94728f97ddf603d23c8c3dd5cae2644fa12d33116e69f49b1644a71bb77b89ae"},
- {file = "frozenlist-1.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c1e8e9033d34c2c9e186e58279879d78c94dd365068a3607af33f2bc99357a53"},
- {file = "frozenlist-1.2.0-cp39-cp39-win32.whl", hash = "sha256:83334e84a290a158c0c4cc4d22e8c7cfe0bba5b76d37f1c2509dabd22acafe15"},
- {file = "frozenlist-1.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:735f386ec522e384f511614c01d2ef9cf799f051353876b4c6fb93ef67a6d1ee"},
- {file = "frozenlist-1.2.0.tar.gz", hash = "sha256:68201be60ac56aff972dc18085800b6ee07973c49103a8aba669dee3d71079de"},
-]
-google-auth = [
- {file = "google-auth-2.3.3.tar.gz", hash = "sha256:d83570a664c10b97a1dc6f8df87e5fdfff012f48f62be131e449c20dfc32630e"},
- {file = "google_auth-2.3.3-py2.py3-none-any.whl", hash = "sha256:a348a50b027679cb7dae98043ac8dbcc1d7951f06d8387496071a1e05a2465c0"},
-]
-httplib2 = [
- {file = "httplib2-0.20.2-py3-none-any.whl", hash = "sha256:6b937120e7d786482881b44b8eec230c1ee1c5c1d06bce8cc865f25abbbf713b"},
- {file = "httplib2-0.20.2.tar.gz", hash = "sha256:e404681d2fbcec7506bcb52c503f2b021e95bee0ef7d01e5c221468a2406d8dc"},
-]
-idna = [
- {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
- {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
-]
-ipython = [
- {file = "ipython-7.31.0-py3-none-any.whl", hash = "sha256:4c4234cdcc6b8f87c5b5c7af9899aa696ac5cfcf0e9f6d0688018bbee5c73bce"},
- {file = "ipython-7.31.0.tar.gz", hash = "sha256:346c74db7312c41fa566d3be45d2e759a528dcc2994fe48aac1a03a70cd668a3"},
-]
-iso8601 = [
- {file = "iso8601-1.0.2-py3-none-any.whl", hash = "sha256:d7bc01b1c2a43b259570bb307f057abc578786ea734ba2b87b836c5efc5bd443"},
- {file = "iso8601-1.0.2.tar.gz", hash = "sha256:27f503220e6845d9db954fb212b95b0362d8b7e6c1b2326a87061c3de93594b1"},
-]
-jedi = [
- {file = "jedi-0.18.1-py2.py3-none-any.whl", hash = "sha256:637c9635fcf47945ceb91cd7f320234a7be540ded6f3e99a50cb6febdfd1ba8d"},
- {file = "jedi-0.18.1.tar.gz", hash = "sha256:74137626a64a99c8eb6ae5832d99b3bdd7d29a3850fe2aa80a4126b2a7d949ab"},
-]
-kopf = [
- {file = "kopf-1.35.3-py3-none-any.whl", hash = "sha256:cff46d874d087d3a86b7ad9ce2e75a0312d9e7a334964d69cc1a63a3fe0e15c8"},
- {file = "kopf-1.35.3.tar.gz", hash = "sha256:3f5d945549902a46e86a99eee604f9efc561af75ac9666eee462ed0319eccc06"},
-]
-kubernetes = [
- {file = "kubernetes-21.7.0-py2.py3-none-any.whl", hash = "sha256:044c20253f8577491a87af8f9edea1f929ed6d62ce306376a6cb8aed24e572c5"},
- {file = "kubernetes-21.7.0.tar.gz", hash = "sha256:c9849afc2eafdce60efa210049ee7a94e7ef6cf3a7afa14a69b3bf0447825977"},
-]
-matplotlib-inline = [
- {file = "matplotlib-inline-0.1.3.tar.gz", hash = "sha256:a04bfba22e0d1395479f866853ec1ee28eea1485c1d69a6faf00dc3e24ff34ee"},
- {file = "matplotlib_inline-0.1.3-py3-none-any.whl", hash = "sha256:aed605ba3b72462d64d475a21a9296f400a19c4f74a31b59103d2a99ffd5aa5c"},
-]
-multidict = [
- {file = "multidict-5.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3822c5894c72e3b35aae9909bef66ec83e44522faf767c0ad39e0e2de11d3b55"},
- {file = "multidict-5.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:28e6d883acd8674887d7edc896b91751dc2d8e87fbdca8359591a13872799e4e"},
- {file = "multidict-5.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b61f85101ef08cbbc37846ac0e43f027f7844f3fade9b7f6dd087178caedeee7"},
- {file = "multidict-5.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9b668c065968c5979fe6b6fa6760bb6ab9aeb94b75b73c0a9c1acf6393ac3bf"},
- {file = "multidict-5.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:517d75522b7b18a3385726b54a081afd425d4f41144a5399e5abd97ccafdf36b"},
- {file = "multidict-5.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1b4ac3ba7a97b35a5ccf34f41b5a8642a01d1e55454b699e5e8e7a99b5a3acf5"},
- {file = "multidict-5.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:df23c83398715b26ab09574217ca21e14694917a0c857e356fd39e1c64f8283f"},
- {file = "multidict-5.2.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e58a9b5cc96e014ddf93c2227cbdeca94b56a7eb77300205d6e4001805391747"},
- {file = "multidict-5.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f76440e480c3b2ca7f843ff8a48dc82446b86ed4930552d736c0bac507498a52"},
- {file = "multidict-5.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cfde464ca4af42a629648c0b0d79b8f295cf5b695412451716531d6916461628"},
- {file = "multidict-5.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0fed465af2e0eb6357ba95795d003ac0bdb546305cc2366b1fc8f0ad67cc3fda"},
- {file = "multidict-5.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:b70913cbf2e14275013be98a06ef4b412329fe7b4f83d64eb70dce8269ed1e1a"},
- {file = "multidict-5.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a5635bcf1b75f0f6ef3c8a1ad07b500104a971e38d3683167b9454cb6465ac86"},
- {file = "multidict-5.2.0-cp310-cp310-win32.whl", hash = "sha256:77f0fb7200cc7dedda7a60912f2059086e29ff67cefbc58d2506638c1a9132d7"},
- {file = "multidict-5.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:9416cf11bcd73c861267e88aea71e9fcc35302b3943e45e1dbb4317f91a4b34f"},
- {file = "multidict-5.2.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:fd77c8f3cba815aa69cb97ee2b2ef385c7c12ada9c734b0f3b32e26bb88bbf1d"},
- {file = "multidict-5.2.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ec9aea6223adf46999f22e2c0ab6cf33f5914be604a404f658386a8f1fba37"},
- {file = "multidict-5.2.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e5283c0a00f48e8cafcecadebfa0ed1dac8b39e295c7248c44c665c16dc1138b"},
- {file = "multidict-5.2.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5f79c19c6420962eb17c7e48878a03053b7ccd7b69f389d5831c0a4a7f1ac0a1"},
- {file = "multidict-5.2.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e4a67f1080123de76e4e97a18d10350df6a7182e243312426d508712e99988d4"},
- {file = "multidict-5.2.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:94b117e27efd8e08b4046c57461d5a114d26b40824995a2eb58372b94f9fca02"},
- {file = "multidict-5.2.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:2e77282fd1d677c313ffcaddfec236bf23f273c4fba7cdf198108f5940ae10f5"},
- {file = "multidict-5.2.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:116347c63ba049c1ea56e157fa8aa6edaf5e92925c9b64f3da7769bdfa012858"},
- {file = "multidict-5.2.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:dc3a866cf6c13d59a01878cd806f219340f3e82eed514485e094321f24900677"},
- {file = "multidict-5.2.0-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:ac42181292099d91217a82e3fa3ce0e0ddf3a74fd891b7c2b347a7f5aa0edded"},
- {file = "multidict-5.2.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:f0bb0973f42ffcb5e3537548e0767079420aefd94ba990b61cf7bb8d47f4916d"},
- {file = "multidict-5.2.0-cp36-cp36m-win32.whl", hash = "sha256:ea21d4d5104b4f840b91d9dc8cbc832aba9612121eaba503e54eaab1ad140eb9"},
- {file = "multidict-5.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:e6453f3cbeb78440747096f239d282cc57a2997a16b5197c9bc839099e1633d0"},
- {file = "multidict-5.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d3def943bfd5f1c47d51fd324df1e806d8da1f8e105cc7f1c76a1daf0f7e17b0"},
- {file = "multidict-5.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35591729668a303a02b06e8dba0eb8140c4a1bfd4c4b3209a436a02a5ac1de11"},
- {file = "multidict-5.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8cacda0b679ebc25624d5de66c705bc53dcc7c6f02a7fb0f3ca5e227d80422"},
- {file = "multidict-5.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:baf1856fab8212bf35230c019cde7c641887e3fc08cadd39d32a421a30151ea3"},
- {file = "multidict-5.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a43616aec0f0d53c411582c451f5d3e1123a68cc7b3475d6f7d97a626f8ff90d"},
- {file = "multidict-5.2.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:25cbd39a9029b409167aa0a20d8a17f502d43f2efebfe9e3ac019fe6796c59ac"},
- {file = "multidict-5.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0a2cbcfbea6dc776782a444db819c8b78afe4db597211298dd8b2222f73e9cd0"},
- {file = "multidict-5.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3d2d7d1fff8e09d99354c04c3fd5b560fb04639fd45926b34e27cfdec678a704"},
- {file = "multidict-5.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a37e9a68349f6abe24130846e2f1d2e38f7ddab30b81b754e5a1fde32f782b23"},
- {file = "multidict-5.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:637c1896497ff19e1ee27c1c2c2ddaa9f2d134bbb5e0c52254361ea20486418d"},
- {file = "multidict-5.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9815765f9dcda04921ba467957be543423e5ec6a1136135d84f2ae092c50d87b"},
- {file = "multidict-5.2.0-cp37-cp37m-win32.whl", hash = "sha256:8b911d74acdc1fe2941e59b4f1a278a330e9c34c6c8ca1ee21264c51ec9b67ef"},
- {file = "multidict-5.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:380b868f55f63d048a25931a1632818f90e4be71d2081c2338fcf656d299949a"},
- {file = "multidict-5.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e7d81ce5744757d2f05fc41896e3b2ae0458464b14b5a2c1e87a6a9d69aefaa8"},
- {file = "multidict-5.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d1d55cdf706ddc62822d394d1df53573d32a7a07d4f099470d3cb9323b721b6"},
- {file = "multidict-5.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a4771d0d0ac9d9fe9e24e33bed482a13dfc1256d008d101485fe460359476065"},
- {file = "multidict-5.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da7d57ea65744d249427793c042094c4016789eb2562576fb831870f9c878d9e"},
- {file = "multidict-5.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdd68778f96216596218b4e8882944d24a634d984ee1a5a049b300377878fa7c"},
- {file = "multidict-5.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ecc99bce8ee42dcad15848c7885197d26841cb24fa2ee6e89d23b8993c871c64"},
- {file = "multidict-5.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:067150fad08e6f2dd91a650c7a49ba65085303fcc3decbd64a57dc13a2733031"},
- {file = "multidict-5.2.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:78c106b2b506b4d895ddc801ff509f941119394b89c9115580014127414e6c2d"},
- {file = "multidict-5.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e6c4fa1ec16e01e292315ba76eb1d012c025b99d22896bd14a66628b245e3e01"},
- {file = "multidict-5.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b227345e4186809d31f22087d0265655114af7cda442ecaf72246275865bebe4"},
- {file = "multidict-5.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:06560fbdcf22c9387100979e65b26fba0816c162b888cb65b845d3def7a54c9b"},
- {file = "multidict-5.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:7878b61c867fb2df7a95e44b316f88d5a3742390c99dfba6c557a21b30180cac"},
- {file = "multidict-5.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:246145bff76cc4b19310f0ad28bd0769b940c2a49fc601b86bfd150cbd72bb22"},
- {file = "multidict-5.2.0-cp38-cp38-win32.whl", hash = "sha256:c30ac9f562106cd9e8071c23949a067b10211917fdcb75b4718cf5775356a940"},
- {file = "multidict-5.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:f19001e790013ed580abfde2a4465388950728861b52f0da73e8e8a9418533c0"},
- {file = "multidict-5.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c1ff762e2ee126e6f1258650ac641e2b8e1f3d927a925aafcfde943b77a36d24"},
- {file = "multidict-5.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bd6c9c50bf2ad3f0448edaa1a3b55b2e6866ef8feca5d8dbec10ec7c94371d21"},
- {file = "multidict-5.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc66d4016f6e50ed36fb39cd287a3878ffcebfa90008535c62e0e90a7ab713ae"},
- {file = "multidict-5.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9acb76d5f3dd9421874923da2ed1e76041cb51b9337fd7f507edde1d86535d6"},
- {file = "multidict-5.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dfc924a7e946dd3c6360e50e8f750d51e3ef5395c95dc054bc9eab0f70df4f9c"},
- {file = "multidict-5.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:32fdba7333eb2351fee2596b756d730d62b5827d5e1ab2f84e6cbb287cc67fe0"},
- {file = "multidict-5.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:b9aad49466b8d828b96b9e3630006234879c8d3e2b0a9d99219b3121bc5cdb17"},
- {file = "multidict-5.2.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:93de39267c4c676c9ebb2057e98a8138bade0d806aad4d864322eee0803140a0"},
- {file = "multidict-5.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f9bef5cff994ca3026fcc90680e326d1a19df9841c5e3d224076407cc21471a1"},
- {file = "multidict-5.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:5f841c4f14331fd1e36cbf3336ed7be2cb2a8f110ce40ea253e5573387db7621"},
- {file = "multidict-5.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:38ba256ee9b310da6a1a0f013ef4e422fca30a685bcbec86a969bd520504e341"},
- {file = "multidict-5.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:3bc3b1621b979621cee9f7b09f024ec76ec03cc365e638126a056317470bde1b"},
- {file = "multidict-5.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6ee908c070020d682e9b42c8f621e8bb10c767d04416e2ebe44e37d0f44d9ad5"},
- {file = "multidict-5.2.0-cp39-cp39-win32.whl", hash = "sha256:1c7976cd1c157fa7ba5456ae5d31ccdf1479680dc9b8d8aa28afabc370df42b8"},
- {file = "multidict-5.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:c9631c642e08b9fff1c6255487e62971d8b8e821808ddd013d8ac058087591ac"},
- {file = "multidict-5.2.0.tar.gz", hash = "sha256:0dd1c93edb444b33ba2274b66f63def8a327d607c6c790772f448a53b6ea59ce"},
-]
-oauth2client = [
- {file = "oauth2client-4.1.3-py2.py3-none-any.whl", hash = "sha256:b8a81cc5d60e2d364f0b1b98f958dbd472887acaf1a5b05e21c28c31a2d6d3ac"},
- {file = "oauth2client-4.1.3.tar.gz", hash = "sha256:d486741e451287f69568a4d26d70d9acd73a2bbfa275746c535b4209891cccc6"},
-]
-oauthlib = [
- {file = "oauthlib-3.1.1-py2.py3-none-any.whl", hash = "sha256:42bf6354c2ed8c6acb54d971fce6f88193d97297e18602a3a886603f9d7730cc"},
- {file = "oauthlib-3.1.1.tar.gz", hash = "sha256:8f0215fcc533dd8dd1bee6f4c412d4f0cd7297307d43ac61666389e3bc3198a3"},
-]
-parso = [
- {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"},
- {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"},
-]
-pexpect = [
- {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"},
- {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"},
-]
-pickleshare = [
- {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"},
- {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"},
-]
-prompt-toolkit = [
- {file = "prompt_toolkit-3.0.24-py3-none-any.whl", hash = "sha256:e56f2ff799bacecd3e88165b1e2f5ebf9bcd59e80e06d395fa0cc4b8bd7bb506"},
- {file = "prompt_toolkit-3.0.24.tar.gz", hash = "sha256:1bb05628c7d87b645974a1bad3f17612be0c29fa39af9f7688030163f680bad6"},
-]
-ptyprocess = [
- {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
- {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
-]
-pyasn1 = [
- {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"},
- {file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"},
- {file = "pyasn1-0.4.8-py2.6.egg", hash = "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00"},
- {file = "pyasn1-0.4.8-py2.7.egg", hash = "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8"},
- {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"},
- {file = "pyasn1-0.4.8-py3.1.egg", hash = "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86"},
- {file = "pyasn1-0.4.8-py3.2.egg", hash = "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7"},
- {file = "pyasn1-0.4.8-py3.3.egg", hash = "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576"},
- {file = "pyasn1-0.4.8-py3.4.egg", hash = "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12"},
- {file = "pyasn1-0.4.8-py3.5.egg", hash = "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2"},
- {file = "pyasn1-0.4.8-py3.6.egg", hash = "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359"},
- {file = "pyasn1-0.4.8-py3.7.egg", hash = "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776"},
- {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"},
-]
-pyasn1-modules = [
- {file = "pyasn1-modules-0.2.8.tar.gz", hash = "sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e"},
- {file = "pyasn1_modules-0.2.8-py2.4.egg", hash = "sha256:0fe1b68d1e486a1ed5473f1302bd991c1611d319bba158e98b106ff86e1d7199"},
- {file = "pyasn1_modules-0.2.8-py2.5.egg", hash = "sha256:fe0644d9ab041506b62782e92b06b8c68cca799e1a9636ec398675459e031405"},
- {file = "pyasn1_modules-0.2.8-py2.6.egg", hash = "sha256:a99324196732f53093a84c4369c996713eb8c89d360a496b599fb1a9c47fc3eb"},
- {file = "pyasn1_modules-0.2.8-py2.7.egg", hash = "sha256:0845a5582f6a02bb3e1bde9ecfc4bfcae6ec3210dd270522fee602365430c3f8"},
- {file = "pyasn1_modules-0.2.8-py2.py3-none-any.whl", hash = "sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74"},
- {file = "pyasn1_modules-0.2.8-py3.1.egg", hash = "sha256:f39edd8c4ecaa4556e989147ebf219227e2cd2e8a43c7e7fcb1f1c18c5fd6a3d"},
- {file = "pyasn1_modules-0.2.8-py3.2.egg", hash = "sha256:b80486a6c77252ea3a3e9b1e360bc9cf28eaac41263d173c032581ad2f20fe45"},
- {file = "pyasn1_modules-0.2.8-py3.3.egg", hash = "sha256:65cebbaffc913f4fe9e4808735c95ea22d7a7775646ab690518c056784bc21b4"},
- {file = "pyasn1_modules-0.2.8-py3.4.egg", hash = "sha256:15b7c67fabc7fc240d87fb9aabf999cf82311a6d6fb2c70d00d3d0604878c811"},
- {file = "pyasn1_modules-0.2.8-py3.5.egg", hash = "sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed"},
- {file = "pyasn1_modules-0.2.8-py3.6.egg", hash = "sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0"},
- {file = "pyasn1_modules-0.2.8-py3.7.egg", hash = "sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd"},
-]
-pygments = [
- {file = "Pygments-2.11.2-py3-none-any.whl", hash = "sha256:44238f1b60a76d78fc8ca0528ee429702aae011c265fe6a8dd8b63049ae41c65"},
- {file = "Pygments-2.11.2.tar.gz", hash = "sha256:4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a"},
-]
-pykube = [
- {file = "pykube-0.15.0-py2.py3-none-any.whl", hash = "sha256:8d50b09b63d5e754f3379032be58bed035692e2e308a0b35e57b0074b3f90bb9"},
- {file = "pykube-0.15.0.tar.gz", hash = "sha256:e53800d0d45f13911aa4ebca8463ae5cc82afad461f6575a0a217d2fce5f088b"},
-]
-pykube-ng = [
- {file = "pykube-ng-21.10.0.tar.gz", hash = "sha256:62799fcfd1630c20e2e7fd89f54cd8ab4ec40806f7bf706f3ab50511b98a1c91"},
- {file = "pykube_ng-21.10.0-py3-none-any.whl", hash = "sha256:78defb7f2cf1a38bdd27c7250e0cde22f070e0e4f27e65c6d1414ebe48cf916c"},
-]
-pyparsing = [
- {file = "pyparsing-3.0.6-py3-none-any.whl", hash = "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4"},
- {file = "pyparsing-3.0.6.tar.gz", hash = "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81"},
-]
-python-dateutil = [
- {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
- {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
-]
-python-json-logger = [
- {file = "python-json-logger-2.0.2.tar.gz", hash = "sha256:202a4f29901a4b8002a6d1b958407eeb2dd1d83c18b18b816f5b64476dde9096"},
- {file = "python_json_logger-2.0.2-py3-none-any.whl", hash = "sha256:99310d148f054e858cd5f4258794ed6777e7ad2c3fd7e1c1b527f1cba4d08420"},
-]
-pytz-deprecation-shim = [
- {file = "pytz_deprecation_shim-0.1.0.post0-py2.py3-none-any.whl", hash = "sha256:8314c9692a636c8eb3bda879b9f119e350e93223ae83e70e80c31675a0fdc1a6"},
- {file = "pytz_deprecation_shim-0.1.0.post0.tar.gz", hash = "sha256:af097bae1b616dde5c5744441e2ddc69e74dfdcb0c263129610d85b87445a59d"},
-]
-pyyaml = [
- {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"},
- {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"},
- {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"},
- {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"},
- {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"},
- {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"},
- {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"},
- {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"},
- {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"},
- {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"},
- {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"},
- {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"},
- {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"},
- {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"},
- {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"},
- {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"},
- {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"},
- {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"},
- {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"},
- {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"},
- {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"},
- {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"},
- {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"},
- {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"},
- {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"},
- {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"},
- {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"},
- {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"},
- {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"},
- {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"},
- {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"},
- {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"},
- {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"},
-]
-requests = [
- {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"},
- {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"},
-]
-requests-oauthlib = [
- {file = "requests-oauthlib-1.3.0.tar.gz", hash = "sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a"},
- {file = "requests_oauthlib-1.3.0-py2.py3-none-any.whl", hash = "sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d"},
- {file = "requests_oauthlib-1.3.0-py3.7.egg", hash = "sha256:fa6c47b933f01060936d87ae9327fead68768b69c6c9ea2109c48be30f2d4dbc"},
-]
-rsa = [
- {file = "rsa-4.8-py3-none-any.whl", hash = "sha256:95c5d300c4e879ee69708c428ba566c59478fd653cc3a22243eeb8ed846950bb"},
- {file = "rsa-4.8.tar.gz", hash = "sha256:5c6bd9dc7a543b7fe4304a631f8a8a3b674e2bbfc49c2ae96200cdbe55df6b17"},
-]
-six = [
- {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
- {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
-]
-traitlets = [
- {file = "traitlets-5.1.1-py3-none-any.whl", hash = "sha256:2d313cc50a42cd6c277e7d7dc8d4d7fedd06a2c215f78766ae7b1a66277e0033"},
- {file = "traitlets-5.1.1.tar.gz", hash = "sha256:059f456c5a7c1c82b98c2e8c799f39c9b8128f6d0d46941ee118daace9eb70c7"},
-]
-typing-extensions = [
- {file = "typing_extensions-4.0.1-py3-none-any.whl", hash = "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"},
- {file = "typing_extensions-4.0.1.tar.gz", hash = "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e"},
-]
-tzdata = [
- {file = "tzdata-2021.5-py2.py3-none-any.whl", hash = "sha256:3eee491e22ebfe1e5cfcc97a4137cd70f092ce59144d81f8924a844de05ba8f5"},
- {file = "tzdata-2021.5.tar.gz", hash = "sha256:68dbe41afd01b867894bbdfd54fa03f468cfa4f0086bfb4adcd8de8f24f3ee21"},
-]
-tzlocal = [
- {file = "tzlocal-4.1-py3-none-any.whl", hash = "sha256:28ba8d9fcb6c9a782d6e0078b4f6627af1ea26aeaa32b4eab5324abc7df4149f"},
- {file = "tzlocal-4.1.tar.gz", hash = "sha256:0f28015ac68a5c067210400a9197fc5d36ba9bc3f8eaf1da3cbd59acdfed9e09"},
-]
-urllib3 = [
- {file = "urllib3-1.26.8-py2.py3-none-any.whl", hash = "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed"},
- {file = "urllib3-1.26.8.tar.gz", hash = "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"},
-]
-wcwidth = [
- {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
- {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
-]
-websocket-client = [
- {file = "websocket-client-1.2.3.tar.gz", hash = "sha256:1315816c0acc508997eb3ae03b9d3ff619c9d12d544c9a9b553704b1cc4f6af5"},
- {file = "websocket_client-1.2.3-py3-none-any.whl", hash = "sha256:2eed4cc58e4d65613ed6114af2f380f7910ff416fc8c46947f6e76b6815f56c0"},
-]
-yarl = [
- {file = "yarl-1.7.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f2a8508f7350512434e41065684076f640ecce176d262a7d54f0da41d99c5a95"},
- {file = "yarl-1.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da6df107b9ccfe52d3a48165e48d72db0eca3e3029b5b8cb4fe6ee3cb870ba8b"},
- {file = "yarl-1.7.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a1d0894f238763717bdcfea74558c94e3bc34aeacd3351d769460c1a586a8b05"},
- {file = "yarl-1.7.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfe4b95b7e00c6635a72e2d00b478e8a28bfb122dc76349a06e20792eb53a523"},
- {file = "yarl-1.7.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c145ab54702334c42237a6c6c4cc08703b6aa9b94e2f227ceb3d477d20c36c63"},
- {file = "yarl-1.7.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ca56f002eaf7998b5fcf73b2421790da9d2586331805f38acd9997743114e98"},
- {file = "yarl-1.7.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1d3d5ad8ea96bd6d643d80c7b8d5977b4e2fb1bab6c9da7322616fd26203d125"},
- {file = "yarl-1.7.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:167ab7f64e409e9bdd99333fe8c67b5574a1f0495dcfd905bc7454e766729b9e"},
- {file = "yarl-1.7.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:95a1873b6c0dd1c437fb3bb4a4aaa699a48c218ac7ca1e74b0bee0ab16c7d60d"},
- {file = "yarl-1.7.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6152224d0a1eb254f97df3997d79dadd8bb2c1a02ef283dbb34b97d4f8492d23"},
- {file = "yarl-1.7.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:5bb7d54b8f61ba6eee541fba4b83d22b8a046b4ef4d8eb7f15a7e35db2e1e245"},
- {file = "yarl-1.7.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:9c1f083e7e71b2dd01f7cd7434a5f88c15213194df38bc29b388ccdf1492b739"},
- {file = "yarl-1.7.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f44477ae29025d8ea87ec308539f95963ffdc31a82f42ca9deecf2d505242e72"},
- {file = "yarl-1.7.2-cp310-cp310-win32.whl", hash = "sha256:cff3ba513db55cc6a35076f32c4cdc27032bd075c9faef31fec749e64b45d26c"},
- {file = "yarl-1.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:c9c6d927e098c2d360695f2e9d38870b2e92e0919be07dbe339aefa32a090265"},
- {file = "yarl-1.7.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9b4c77d92d56a4c5027572752aa35082e40c561eec776048330d2907aead891d"},
- {file = "yarl-1.7.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c01a89a44bb672c38f42b49cdb0ad667b116d731b3f4c896f72302ff77d71656"},
- {file = "yarl-1.7.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c19324a1c5399b602f3b6e7db9478e5b1adf5cf58901996fc973fe4fccd73eed"},
- {file = "yarl-1.7.2-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3abddf0b8e41445426d29f955b24aeecc83fa1072be1be4e0d194134a7d9baee"},
- {file = "yarl-1.7.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6a1a9fe17621af43e9b9fcea8bd088ba682c8192d744b386ee3c47b56eaabb2c"},
- {file = "yarl-1.7.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8b0915ee85150963a9504c10de4e4729ae700af11df0dc5550e6587ed7891e92"},
- {file = "yarl-1.7.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:29e0656d5497733dcddc21797da5a2ab990c0cb9719f1f969e58a4abac66234d"},
- {file = "yarl-1.7.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:bf19725fec28452474d9887a128e98dd67eee7b7d52e932e6949c532d820dc3b"},
- {file = "yarl-1.7.2-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:d6f3d62e16c10e88d2168ba2d065aa374e3c538998ed04996cd373ff2036d64c"},
- {file = "yarl-1.7.2-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:ac10bbac36cd89eac19f4e51c032ba6b412b3892b685076f4acd2de18ca990aa"},
- {file = "yarl-1.7.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:aa32aaa97d8b2ed4e54dc65d241a0da1c627454950f7d7b1f95b13985afd6c5d"},
- {file = "yarl-1.7.2-cp36-cp36m-win32.whl", hash = "sha256:87f6e082bce21464857ba58b569370e7b547d239ca22248be68ea5d6b51464a1"},
- {file = "yarl-1.7.2-cp36-cp36m-win_amd64.whl", hash = "sha256:ac35ccde589ab6a1870a484ed136d49a26bcd06b6a1c6397b1967ca13ceb3913"},
- {file = "yarl-1.7.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a467a431a0817a292121c13cbe637348b546e6ef47ca14a790aa2fa8cc93df63"},
- {file = "yarl-1.7.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ab0c3274d0a846840bf6c27d2c60ba771a12e4d7586bf550eefc2df0b56b3b4"},
- {file = "yarl-1.7.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d260d4dc495c05d6600264a197d9d6f7fc9347f21d2594926202fd08cf89a8ba"},
- {file = "yarl-1.7.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fc4dd8b01a8112809e6b636b00f487846956402834a7fd59d46d4f4267181c41"},
- {file = "yarl-1.7.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c1164a2eac148d85bbdd23e07dfcc930f2e633220f3eb3c3e2a25f6148c2819e"},
- {file = "yarl-1.7.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:67e94028817defe5e705079b10a8438b8cb56e7115fa01640e9c0bb3edf67332"},
- {file = "yarl-1.7.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:89ccbf58e6a0ab89d487c92a490cb5660d06c3a47ca08872859672f9c511fc52"},
- {file = "yarl-1.7.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8cce6f9fa3df25f55521fbb5c7e4a736683148bcc0c75b21863789e5185f9185"},
- {file = "yarl-1.7.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:211fcd65c58bf250fb994b53bc45a442ddc9f441f6fec53e65de8cba48ded986"},
- {file = "yarl-1.7.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c10ea1e80a697cf7d80d1ed414b5cb8f1eec07d618f54637067ae3c0334133c4"},
- {file = "yarl-1.7.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:52690eb521d690ab041c3919666bea13ab9fbff80d615ec16fa81a297131276b"},
- {file = "yarl-1.7.2-cp37-cp37m-win32.whl", hash = "sha256:695ba021a9e04418507fa930d5f0704edbce47076bdcfeeaba1c83683e5649d1"},
- {file = "yarl-1.7.2-cp37-cp37m-win_amd64.whl", hash = "sha256:c17965ff3706beedafd458c452bf15bac693ecd146a60a06a214614dc097a271"},
- {file = "yarl-1.7.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fce78593346c014d0d986b7ebc80d782b7f5e19843ca798ed62f8e3ba8728576"},
- {file = "yarl-1.7.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c2a1ac41a6aa980db03d098a5531f13985edcb451bcd9d00670b03129922cd0d"},
- {file = "yarl-1.7.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:39d5493c5ecd75c8093fa7700a2fb5c94fe28c839c8e40144b7ab7ccba6938c8"},
- {file = "yarl-1.7.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1eb6480ef366d75b54c68164094a6a560c247370a68c02dddb11f20c4c6d3c9d"},
- {file = "yarl-1.7.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ba63585a89c9885f18331a55d25fe81dc2d82b71311ff8bd378fc8004202ff6"},
- {file = "yarl-1.7.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e39378894ee6ae9f555ae2de332d513a5763276a9265f8e7cbaeb1b1ee74623a"},
- {file = "yarl-1.7.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c0910c6b6c31359d2f6184828888c983d54d09d581a4a23547a35f1d0b9484b1"},
- {file = "yarl-1.7.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6feca8b6bfb9eef6ee057628e71e1734caf520a907b6ec0d62839e8293e945c0"},
- {file = "yarl-1.7.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8300401dc88cad23f5b4e4c1226f44a5aa696436a4026e456fe0e5d2f7f486e6"},
- {file = "yarl-1.7.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:788713c2896f426a4e166b11f4ec538b5736294ebf7d5f654ae445fd44270832"},
- {file = "yarl-1.7.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:fd547ec596d90c8676e369dd8a581a21227fe9b4ad37d0dc7feb4ccf544c2d59"},
- {file = "yarl-1.7.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:737e401cd0c493f7e3dd4db72aca11cfe069531c9761b8ea474926936b3c57c8"},
- {file = "yarl-1.7.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:baf81561f2972fb895e7844882898bda1eef4b07b5b385bcd308d2098f1a767b"},
- {file = "yarl-1.7.2-cp38-cp38-win32.whl", hash = "sha256:ede3b46cdb719c794427dcce9d8beb4abe8b9aa1e97526cc20de9bd6583ad1ef"},
- {file = "yarl-1.7.2-cp38-cp38-win_amd64.whl", hash = "sha256:cc8b7a7254c0fc3187d43d6cb54b5032d2365efd1df0cd1749c0c4df5f0ad45f"},
- {file = "yarl-1.7.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:580c1f15500e137a8c37053e4cbf6058944d4c114701fa59944607505c2fe3a0"},
- {file = "yarl-1.7.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3ec1d9a0d7780416e657f1e405ba35ec1ba453a4f1511eb8b9fbab81cb8b3ce1"},
- {file = "yarl-1.7.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3bf8cfe8856708ede6a73907bf0501f2dc4e104085e070a41f5d88e7faf237f3"},
- {file = "yarl-1.7.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1be4bbb3d27a4e9aa5f3df2ab61e3701ce8fcbd3e9846dbce7c033a7e8136746"},
- {file = "yarl-1.7.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:534b047277a9a19d858cde163aba93f3e1677d5acd92f7d10ace419d478540de"},
- {file = "yarl-1.7.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6ddcd80d79c96eb19c354d9dca95291589c5954099836b7c8d29278a7ec0bda"},
- {file = "yarl-1.7.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9bfcd43c65fbb339dc7086b5315750efa42a34eefad0256ba114cd8ad3896f4b"},
- {file = "yarl-1.7.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f64394bd7ceef1237cc604b5a89bf748c95982a84bcd3c4bbeb40f685c810794"},
- {file = "yarl-1.7.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044daf3012e43d4b3538562da94a88fb12a6490652dbc29fb19adfa02cf72eac"},
- {file = "yarl-1.7.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:368bcf400247318382cc150aaa632582d0780b28ee6053cd80268c7e72796dec"},
- {file = "yarl-1.7.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:bab827163113177aee910adb1f48ff7af31ee0289f434f7e22d10baf624a6dfe"},
- {file = "yarl-1.7.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0cba38120db72123db7c58322fa69e3c0efa933040ffb586c3a87c063ec7cae8"},
- {file = "yarl-1.7.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:59218fef177296451b23214c91ea3aba7858b4ae3306dde120224cfe0f7a6ee8"},
- {file = "yarl-1.7.2-cp39-cp39-win32.whl", hash = "sha256:1edc172dcca3f11b38a9d5c7505c83c1913c0addc99cd28e993efeaafdfaa18d"},
- {file = "yarl-1.7.2-cp39-cp39-win_amd64.whl", hash = "sha256:797c2c412b04403d2da075fb93c123df35239cd7b4cc4e0cd9e5839b73f52c58"},
- {file = "yarl-1.7.2.tar.gz", hash = "sha256:45399b46d60c253327a460e99856752009fcee5f5d3c80b2f7c0cae1c38d56dd"},
-]
diff --git a/demos/operator/pyproject.toml b/demos/operator/pyproject.toml
deleted file mode 100644
index e2156a6..0000000
--- a/demos/operator/pyproject.toml
+++ /dev/null
@@ -1,35 +0,0 @@
-# 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.
-#
-[tool.poetry]
-name = "nuvolaris"
-version = "0.1.0"
-description = "Nuvolaris operator"
-authors = ["Michele Sciabarra <mi...@sciabarra.com>"]
-
-[tool.poetry.dependencies]
-python = "^3.9"
-kopf = {extras = ["full-auth"], version = "^1.35.3"}
-PyYAML = "^6.0"
-pykube = "^0.15.0"
-
-[tool.poetry.dev-dependencies]
-ipython = "^7.31.0"
-
-[build-system]
-requires = ["poetry-core>=1.0.0"]
-build-backend = "poetry.core.masonry.api"
diff --git a/demos/operator/transcript.txt b/demos/operator/transcript.txt
deleted file mode 100644
index 279a0da..0000000
--- a/demos/operator/transcript.txt
+++ /dev/null
@@ -1,78 +0,0 @@
-# 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.
-#
-# ----
-
-### Demo CRD
-# check
-kubectl get nodes
-cd lab
-kubectl apply -f demo-ns.yaml
-kubectl config set-context --current --namespace demo
-# create crd and instance
-kubectl apply -f demo-crd.yaml
-kubectl get crd
-kubectl apply -f demo-obj.yaml
-kubectl -n demo get sam
-# cleanup
-kubectl -n demo delete sam/obj
-kubectl -n demo get sam
-
-### Handling creation of objects
-# install and run kopf
-poetry install
-poetry run kopf
-# run demo1.py
-cat demo1.py
-poetry run kopf run demo1.py
-# note the error - add ns
-poetry run kopf run -n demo demo1.py
-# new terminal
-kubectl apply -f demo-obj.yaml
-kubectl -n demo get sam
-kubectl delete -f demo-obj.yaml
-
-### Demo Kustomize
-cat demo-deployment.yaml
-kubectl apply -f demo-deployment.yaml
-kubectl get deploy ; kubectl get po
-kubectl delete deploy demo-deploy
-kubectl get deploy ; kubectl get po
-# kustomize
-cat kustomization.yaml
-cat patch.yaml
-# prepare the customization
-rm -Rvf deploy ; mkdir deploy
-cp demo-deployment.yaml kustomization.yaml patch.yaml deploy
-# apply the kustomization
-kubectl apply -k deploy
-kubectl get deploy ; kubectl get po
-kubectl delete -k deploy
-kubectl get deploy ; kubectl get po
-
-### Demo Operator
-# cleanup
-kubectl delete -f demo-obj.yaml
-poetry run kopf run -n demo demo2.py
-# switch terminal
-cat demo-obj.yaml
-kubectl apply -f demo-obj.yaml
-# checking if it worked
-cat deploy/patch.yaml
-kubectl get deploy
-
-
diff --git a/demos/velero/Taskfile.yml b/demos/velero/Taskfile.yml
deleted file mode 100644
index e7d09c3..0000000
--- a/demos/velero/Taskfile.yml
+++ /dev/null
@@ -1,154 +0,0 @@
-# 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.
-#
-
-version: '3'
-
-env:
- BACKUP_STORAGE_LOCATION_NAME: gcloud-app-1
- PROVIDER_NAME: aws
- PROVIDER_PLUGIN_TAG: v1.6.2
- REGION: us-east-1
- BUCKET: nuvolaris-gc-app-1-backup
- VOLUME_SNAPSHOT_LOCATION_NAME: gc-app-1-volume
-
-tasks:
-
- setup-aws:
- cmds:
- - aws s3api create-bucket --bucket ${BUCKET} --region ${REGION}
- - aws iam create-user --user-name velero-app-1
- - |
- cat > __velero-policy.json <<EOF
- {
- "Version": "2012-10-17",
- "Statement": [
- {
- "Effect": "Allow",
- "Action": [
- "ec2:DescribeVolumes",
- "ec2:DescribeSnapshots",
- "ec2:CreateTags",
- "ec2:CreateVolume",
- "ec2:CreateSnapshot",
- "ec2:DeleteSnapshot"
- ],
- "Resource": "*"
- },
- {
- "Effect": "Allow",
- "Action": [
- "s3:GetObject",
- "s3:DeleteObject",
- "s3:PutObject",
- "s3:AbortMultipartUpload",
- "s3:ListMultipartUploadParts"
- ],
- "Resource": [
- "arn:aws:s3:::${BUCKET}/*"
- ]
- },
- {
- "Effect": "Allow",
- "Action": [
- "s3:ListBucket"
- ],
- "Resource": [
- "arn:aws:s3:::${BUCKET}"
- ]
- }
- ]
- }
- EOF
- - aws iam put-user-policy --user-name velero-saas1 --policy-name velero-saas1-policy --policy-document file://__velero-policy.json
- - aws iam create-access-key --user-name velero-saas1 > __hidden-secret.json
-
- create-aws-key:
- cmds:
- - |
- cat << EOF > __credentials_velero
- [default]
- aws_access_key_id=$ACCESS_KEY_ID
- aws_secret_access_key=$SECRET_ACCESS_KEY
- EOF
- env:
- SECRET_ACCESS_KEY:
- sh: cat __hidden-secret.json | jq '.AccessKey.SecretAccessKey'
- ACCESS_KEY_ID:
- sh: cat __hidden-secret.json | jq '.AccessKey.AccessKeyId'
-
-# Helm related setup
- setup-repo:
- - helm repo add vmware-tanzu https://vmware-tanzu.github.io/helm-charts
- - helm repo update
-
- velero-helm-install:
- - |
- helm install velero vmware-tanzu/velero \
- --set-file credentials.secretContents.cloud=__credentials-velero \
- --set configuration.backupStorageLocation[0].name=${BACKUP_STORAGE_LOCATION_NAME} \
- --set configuration.backupStorageLocation[0].provider=${PROVIDER_NAME} \
- --set configuration.backupStorageLocation[0].bucket=${BUCKET} \
- --set configuration.backupStorageLocation[0].config.region=${REGION} \
- --set configuration.volumeSnapshotLocation[0].name=${VOLUME_SNAPSHOT_LOCATION_NAME} \
- --set configuration.volumeSnapshotLocation[0].provider=${PROVIDER_NAME} \
- --set configuration.volumeSnapshotLocation[0].config.region=${REGION} \
- --set initContainers[0].name=velero-plugin-for-${PROVIDER_NAME} \
- --set initContainers[0].image=velero/velero-plugin-for-${PROVIDER_NAME}:${PROVIDER_PLUGIN_TAG}\
- --set initContainers[0].volumeMounts[0].mountPath=/target \
- --set initContainers[0].volumeMounts[0].name=plugins
-
-# setup via velero cli
- setup-cli:
- cmds:
- - |
- sudo rm -Rf /tmp/velero ;\
- mkdir /tmp/velero ;\
- ARCH="$(dpkg --print-architecture)" ;\
- URL="https://github.com/vmware-tanzu/velero/releases/download/${CLI_VERSION}/velero-${CLI_VERSION}-linux-${ARCH}.tar.gz" ;\
- echo "$URL" ;\
- curl -sL "$URL" | tar xzvf - -C /tmp/velero; \
- sudo mv /tmp/velero/velero-${CLI_VERSION}-linux-amd64/velero /usr/bin
- env:
- CLI_VERSION: v1.10.3
-
- velero-cli-install: |
- velero install \
- --provider ${PROVIDER_NAME} \
- --plugins velero/velero-plugin-for-${PROVIDER_NAME}:${PROVIDER_PLUGIN_TAG} \
- --bucket ${BUCKET} \
- --secret-file ./__credentials_velero \
- --backup-location-config region=${REGION} \
- --snapshot-location-config region=${REGION} \
- --use-node-agent \
- --default-volumes-to-fs-backup
-
- velero-cli-uninstall: |
- velero uninstall
-
- velero-backup-create:
- velero backup create gc-app-1-backup --include-namespaces nuvolaris --csi-snapshot-timeout=20m
-
- velero-backup-schedule:
- velero create schedule gc-app-1-backup --schedule="0 1 * * *" --include-namespaces nuvolaris
-
- velero-backup-trigger:
- velero backup create --from-schedule gc-app-1-backup
-
- velero-backup-delete:
- velero backup delete gc-app-1-backup
-
diff --git a/demos/velero/values.yaml b/demos/velero/values.yaml
deleted file mode 100644
index e412734..0000000
--- a/demos/velero/values.yaml
+++ /dev/null
@@ -1,538 +0,0 @@
-# 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.
-#
-
-##
-## Configuration settings that directly affect the Velero deployment YAML.
-##
-
-# Details of the container image to use in the Velero deployment & daemonset (if
-# enabling node-agent). Required.
-image:
- repository: velero/velero
- tag: v1.11.0
- # Digest value example: sha256:d238835e151cec91c6a811fe3a89a66d3231d9f64d09e5f3c49552672d271f38.
- # If used, it will take precedence over the image.tag.
- # digest:
- pullPolicy: IfNotPresent
- # One or more secrets to be used when pulling images
- imagePullSecrets: []
- # - registrySecretName
-
-# Annotations to add to the Velero deployment's. Optional.
-#
-# If you are using reloader use the following annotation with your VELERO_SECRET_NAME
-annotations: {}
-# secret.reloader.stakater.com/reload: "<VELERO_SECRET_NAME>"
-
-# Labels to add to the Velero deployment's. Optional.
-labels: {}
-
-# Annotations to add to the Velero deployment's pod template. Optional.
-#
-# If using kube2iam or kiam, use the following annotation with your AWS_ACCOUNT_ID
-# and VELERO_ROLE_NAME filled in:
-podAnnotations: {}
- # iam.amazonaws.com/role: "arn:aws:iam::<AWS_ACCOUNT_ID>:role/<VELERO_ROLE_NAME>"
-
-# Additional pod labels for Velero deployment's template. Optional
-# ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
-podLabels: {}
-
-# Resource requests/limits to specify for the Velero deployment.
-# https://velero.io/docs/v1.6/customize-installation/#customize-resource-requests-and-limits
-resources:
- requests:
- cpu: 500m
- memory: 128Mi
- limits:
- cpu: 1000m
- memory: 512Mi
-
-# Configure the dnsPolicy of the Velero deployment
-# See: https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy
-dnsPolicy: ClusterFirst
-
-# Init containers to add to the Velero deployment's pod spec. At least one plugin provider image is required.
-# If the value is a string then it is evaluated as a template.
-initContainers:
- # - name: velero-plugin-for-csi
- # image: velero/velero-plugin-for-csi:v0.5.0
- # imagePullPolicy: IfNotPresent
- # volumeMounts:
- # - mountPath: /target
- # name: plugins
- # - name: velero-plugin-for-aws
- # image: velero/velero-plugin-for-aws:v1.7.0
- # imagePullPolicy: IfNotPresent
- # volumeMounts:
- # - mountPath: /target
- # name: plugins
-
-# SecurityContext to use for the Velero deployment. Optional.
-# Set fsGroup for `AWS IAM Roles for Service Accounts`
-# see more informations at: https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html
-podSecurityContext: {}
- # fsGroup: 1337
-
-# Container Level Security Context for the 'velero' container of the Velero deployment. Optional.
-# See: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container
-containerSecurityContext: {}
- # allowPrivilegeEscalation: false
- # capabilities:
- # drop: ["ALL"]
- # add: []
- # readOnlyRootFilesystem: true
-
-# Container Lifecycle Hooks to use for the Velero deployment. Optional.
-lifecycle: {}
-
-# Pod priority class name to use for the Velero deployment. Optional.
-priorityClassName: ""
-
-# The number of seconds to allow for graceful termination of the pod. Optional.
-terminationGracePeriodSeconds: 3600
-
-# Tolerations to use for the Velero deployment. Optional.
-tolerations: []
-
-# Affinity to use for the Velero deployment. Optional.
-affinity: {}
-
-# Node selector to use for the Velero deployment. Optional.
-nodeSelector: {}
-
-# DNS configuration to use for the Velero deployment. Optional.
-dnsConfig: {}
-
-# Extra volumes for the Velero deployment. Optional.
-extraVolumes: []
-
-# Extra volumeMounts for the Velero deployment. Optional.
-extraVolumeMounts: []
-
-# Extra K8s manifests to deploy
-extraObjects: []
- # - apiVersion: secrets-store.csi.x-k8s.io/v1
- # kind: SecretProviderClass
- # metadata:
- # name: velero-secrets-store
- # spec:
- # provider: aws
- # parameters:
- # objects: |
- # - objectName: "velero"
- # objectType: "secretsmanager"
- # jmesPath:
- # - path: "access_key"
- # objectAlias: "access_key"
- # - path: "secret_key"
- # objectAlias: "secret_key"
- # secretObjects:
- # - data:
- # - key: access_key
- # objectName: client-id
- # - key: client-secret
- # objectName: client-secret
- # secretName: velero-secrets-store
- # type: Opaque
-
-# Settings for Velero's prometheus metrics. Enabled by default.
-metrics:
- enabled: false
- scrapeInterval: 30s
- scrapeTimeout: 10s
-
- # service metadata if metrics are enabled
- service:
- annotations: {}
- labels: {}
-
- # Pod annotations for Prometheus
- podAnnotations:
- prometheus.io/scrape: "false"
- prometheus.io/port: "8085"
- prometheus.io/path: "/metrics"
-
- serviceMonitor:
- autodetect: true
- enabled: false
- annotations: {}
- additionalLabels: {}
- # ServiceMonitor namespace. Default to Velero namespace.
- # namespace:
- # ServiceMonitor connection scheme. Defaults to HTTP.
- # scheme: ""
- # ServiceMonitor connection tlsConfig. Defaults to {}.
- # tlsConfig: {}
- nodeAgentPodMonitor:
- autodetect: true
- enabled: false
- annotations: {}
- additionalLabels: {}
- # ServiceMonitor namespace. Default to Velero namespace.
- # namespace:
- # ServiceMonitor connection scheme. Defaults to HTTP.
- # scheme: ""
- # ServiceMonitor connection tlsConfig. Defaults to {}.
- # tlsConfig: {}
-
- prometheusRule:
- enabled: false
- # Additional labels to add to deployed PrometheusRule
- additionalLabels: {}
- # PrometheusRule namespace. Defaults to Velero namespace.
- # namespace: ""
- # Rules to be deployed
- spec: []
- # - alert: VeleroBackupPartialFailures
- # annotations:
- # message: Velero backup {{ $labels.schedule }} has {{ $value | humanizePercentage }} partialy failed backups.
- # expr: |-
- # velero_backup_partial_failure_total{schedule!=""} / velero_backup_attempt_total{schedule!=""} > 0.25
- # for: 15m
- # labels:
- # severity: warning
- # - alert: VeleroBackupFailures
- # annotations:
- # message: Velero backup {{ $labels.schedule }} has {{ $value | humanizePercentage }} failed backups.
- # expr: |-
- # velero_backup_failure_total{schedule!=""} / velero_backup_attempt_total{schedule!=""} > 0.25
- # for: 15m
- # labels:
- # severity: warning
-
-kubectl:
- image:
- repository: docker.io/bitnami/kubectl
- # Digest value example: sha256:d238835e151cec91c6a811fe3a89a66d3231d9f64d09e5f3c49552672d271f38.
- # If used, it will take precedence over the kubectl.image.tag.
- # digest:
- # kubectl image tag. If used, it will take precedence over the cluster Kubernetes version.
- # tag: 1.16.15
- # Container Level Security Context for the 'kubectl' container of the crd jobs. Optional.
- # See: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container
- containerSecurityContext: {}
- # Resource requests/limits to specify for the upgrade/cleanup job. Optional
- resources: {}
- # Annotations to set for the upgrade/cleanup job. Optional.
- annotations: {}
- # Labels to set for the upgrade/cleanup job. Optional.
- labels: {}
-
-# This job upgrades the CRDs.
-upgradeCRDs: true
-
-# This job is meant primarily for cleaning up CRDs on CI systems.
-# Using this on production systems, especially those that have multiple releases of Velero, will be destructive.
-cleanUpCRDs: false
-
-##
-## End of deployment-related settings.
-##
-
-
-##
-## Parameters for the `default` BackupStorageLocation and VolumeSnapshotLocation,
-## and additional server settings.
-##
-configuration:
- # Parameters for the BackupStorageLocation(s). Configure multiple by adding other element(s) to the backupStorageLocation slice.
- # See https://velero.io/docs/v1.6/api-types/backupstoragelocation/
- backupStorageLocation:
- # name is the name of the backup storage location where backups should be stored. If a name is not provided,
- # a backup storage location will be created with the name "default". Optional.
- - name:
- # provider is the name for the backup storage location provider.
- provider:
- # bucket is the name of the bucket to store backups in. Required.
- bucket:
- # caCert defines a base64 encoded CA bundle to use when verifying TLS connections to the provider. Optional.
- caCert:
- # prefix is the directory under which all Velero data should be stored within the bucket. Optional.
- prefix:
- # default indicates this location is the default backup storage location. Optional.
- default:
- # accessMode determines if velero can write to this backup storage location. Optional.
- # default to ReadWrite, ReadOnly is used during migrations and restores.
- accessMode: ReadWrite
- credential:
- # name of the secret used by this backupStorageLocation.
- name:
- # name of key that contains the secret data to be used.
- key:
- # Additional provider-specific configuration. See link above
- # for details of required/optional fields for your provider.
- config: {}
- # region:
- # s3ForcePathStyle:
- # s3Url:
- # kmsKeyId:
- # resourceGroup:
- # The ID of the subscription containing the storage account, if different from the cluster’s subscription. (Azure only)
- # subscriptionId:
- # storageAccount:
- # publicUrl:
- # Name of the GCP service account to use for this backup storage location. Specify the
- # service account here if you want to use workload identity instead of providing the key file.(GCP only)
- # serviceAccount:
- # Option to skip certificate validation or not if insecureSkipTLSVerify is set to be true, the client side should set the
- # flag. For Velero client Command like velero backup describe, velero backup logs needs to add the flag --insecure-skip-tls-verify
- # insecureSkipTLSVerify:
-
- # Parameters for the VolumeSnapshotLocation(s). Configure multiple by adding other element(s) to the volumeSnapshotLocation slice.
- # See https://velero.io/docs/v1.6/api-types/volumesnapshotlocation/
- volumeSnapshotLocation:
- # name is the name of the volume snapshot location where snapshots are being taken. Required.
- - name:
- # provider is the name for the volume snapshot provider.
- provider:
- # Additional provider-specific configuration. See link above
- # for details of required/optional fields for your provider.
- config: {}
- # region:
- # apiTimeout:
- # resourceGroup:
- # The ID of the subscription where volume snapshots should be stored, if different from the cluster’s subscription. If specified, also requires `configuration.volumeSnapshotLocation.config.resourceGroup`to be set. (Azure only)
- # subscriptionId:
- # incremental:
- # snapshotLocation:
- # project:
-
- # These are server-level settings passed as CLI flags to the `velero server` command. Velero
- # uses default values if they're not passed in, so they only need to be explicitly specified
- # here if using a non-default value. The `velero server` default values are shown in the
- # comments below.
- # --------------------
- # `velero server` default: restic
- uploaderType:
- # `velero server` default: 1m
- backupSyncPeriod:
- # `velero server` default: 4h
- fsBackupTimeout:
- # `velero server` default: 30
- clientBurst:
- # `velero server` default: 500
- clientPageSize:
- # `velero server` default: 20.0
- clientQPS:
- # Name of the default backup storage location. Default: default
- defaultBackupStorageLocation:
- # How long to wait by default before backups can be garbage collected. Default: 72h
- defaultBackupTTL:
- # Name of the default volume snapshot location.
- defaultVolumeSnapshotLocations:
- # `velero server` default: empty
- disableControllers:
- # `velero server` default: 1h
- garbageCollectionFrequency:
- # Set log-format for Velero pod. Default: text. Other option: json.
- logFormat:
- # Set log-level for Velero pod. Default: info. Other options: debug, warning, error, fatal, panic.
- logLevel:
- # The address to expose prometheus metrics. Default: :8085
- metricsAddress:
- # Directory containing Velero plugins. Default: /plugins
- pluginDir:
- # The address to expose the pprof profiler. Default: localhost:6060
- profilerAddress:
- # `velero server` default: false
- restoreOnlyMode:
- # `velero server` default: customresourcedefinitions,namespaces,storageclasses,volumesnapshotclass.snapshot.storage.k8s.io,volumesnapshotcontents.snapshot.storage.k8s.io,volumesnapshots.snapshot.storage.k8s.io,persistentvolumes,persistentvolumeclaims,secrets,configmaps,serviceaccounts,limitranges,pods,replicasets.apps,clusterclasses.cluster.x-k8s.io,clusters.cluster.x-k8s.io,clusterresourcesets.addons.cluster.x-k8s.io
- restoreResourcePriorities:
- # `velero server` default: 1m
- storeValidationFrequency:
- # How long to wait on persistent volumes and namespaces to terminate during a restore before timing out. Default: 10m
- terminatingResourceTimeout:
- # Comma separated list of velero feature flags. default: empty
- # features: EnableCSI
- features:
- # `velero server` default: velero
- namespace:
-
- # additional key/value pairs to be used as environment variables such as "AWS_CLUSTER_NAME: 'yourcluster.domain.tld'"
- extraEnvVars: {}
-
- # Set true for backup all pod volumes without having to apply annotation on the pod when used file system backup Default: false.
- defaultVolumesToFsBackup:
-
- # How often repository maintain is run for repositories by default.
- defaultRepoMaintainFrequency:
-
-##
-## End of backup/snapshot location settings.
-##
-
-
-##
-## Settings for additional Velero resources.
-##
-
-rbac:
- # Whether to create the Velero role and role binding to give all permissions to the namespace to Velero.
- create: true
- # Whether to create the cluster role binding to give administrator permissions to Velero
- clusterAdministrator: true
- # Name of the ClusterRole.
- clusterAdministratorName: cluster-admin
-
-# Information about the Kubernetes service account Velero uses.
-serviceAccount:
- server:
- create: true
- name:
- annotations:
- labels:
-
-# Info about the secret to be used by the Velero deployment, which
-# should contain credentials for the cloud provider IAM account you've
-# set up for Velero.
-credentials:
- # Whether a secret should be used. Set to false if, for examples:
- # - using kube2iam or kiam to provide AWS IAM credentials instead of providing the key file. (AWS only)
- # - using workload identity instead of providing the key file. (GCP only)
- useSecret: true
- # Name of the secret to create if `useSecret` is true and `existingSecret` is empty
- name:
- # Name of a pre-existing secret (if any) in the Velero namespace
- # that should be used to get IAM account credentials. Optional.
- existingSecret:
- # Data to be stored in the Velero secret, if `useSecret` is true and `existingSecret` is empty.
- # As of the current Velero release, Velero only uses one secret key/value at a time.
- # The key must be named `cloud`, and the value corresponds to the entire content of your IAM credentials file.
- # Note that the format will be different for different providers, please check their documentation.
- # Here is a list of documentation for plugins maintained by the Velero team:
- # [AWS] https://github.com/vmware-tanzu/velero-plugin-for-aws/blob/main/README.md
- # [GCP] https://github.com/vmware-tanzu/velero-plugin-for-gcp/blob/main/README.md
- # [Azure] https://github.com/vmware-tanzu/velero-plugin-for-microsoft-azure/blob/main/README.md
- secretContents: {}
- # cloud: |
- # [default]
- # aws_access_key_id=<REDACTED>
- # aws_secret_access_key=<REDACTED>
- # additional key/value pairs to be used as environment variables such as "DIGITALOCEAN_TOKEN: <your-key>". Values will be stored in the secret.
- extraEnvVars: {}
- # Name of a pre-existing secret (if any) in the Velero namespace
- # that will be used to load environment variables into velero and node-agent.
- # Secret should be in format - https://kubernetes.io/docs/concepts/configuration/secret/#use-case-as-container-environment-variables
- extraSecretRef: ""
-
-# Whether to create backupstoragelocation crd, if false => do not create a default backup location
-backupsEnabled: true
-# Whether to create volumesnapshotlocation crd, if false => disable snapshot feature
-snapshotsEnabled: true
-
-# Whether to deploy the node-agent daemonset.
-deployNodeAgent: false
-
-nodeAgent:
- podVolumePath: /var/lib/kubelet/pods
- privileged: false
- # Pod priority class name to use for the node-agent daemonset. Optional.
- priorityClassName: ""
- # Resource requests/limits to specify for the node-agent daemonset deployment. Optional.
- # https://velero.io/docs/v1.6/customize-installation/#customize-resource-requests-and-limits
- resources:
- requests:
- cpu: 500m
- memory: 512Mi
- limits:
- cpu: 1000m
- memory: 1024Mi
-
- # Tolerations to use for the node-agent daemonset. Optional.
- tolerations: []
-
- # Annotations to set for the node-agent daemonset. Optional.
- annotations: {}
-
- # labels to set for the node-agent daemonset. Optional.
- labels: {}
-
- # will map /scratch to emptyDir. Set to false and specify your own volume
- # via extraVolumes and extraVolumeMounts that maps to /scratch
- # if you don't want to use emptyDir.
- useScratchEmptyDir: true
-
- # Extra volumes for the node-agent daemonset. Optional.
- extraVolumes: []
-
- # Extra volumeMounts for the node-agent daemonset. Optional.
- extraVolumeMounts: []
-
- # Key/value pairs to be used as environment variables for the node-agent daemonset. Optional.
- extraEnvVars: {}
-
- # Configure the dnsPolicy of the node-agent daemonset
- # See: https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy
- dnsPolicy: ClusterFirst
-
- # SecurityContext to use for the Velero deployment. Optional.
- # Set fsGroup for `AWS IAM Roles for Service Accounts`
- # see more informations at: https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html
- podSecurityContext:
- runAsUser: 0
- # fsGroup: 1337
-
- # Container Level Security Context for the 'node-agent' container of the node-agent daemonset. Optional.
- # See: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container
- containerSecurityContext: {}
-
- # Container Lifecycle Hooks to use for the node-agent daemonset. Optional.
- lifecycle: {}
-
- # Node selector to use for the node-agent daemonset. Optional.
- nodeSelector: {}
-
- # Affinity to use with node-agent daemonset. Optional.
- affinity: {}
-
- # DNS configuration to use for the node-agent daemonset. Optional.
- dnsConfig: {}
-
-# Backup schedules to create.
-# Eg:
-# schedules:
-# mybackup:
-# disabled: false
-# labels:
-# myenv: foo
-# annotations:
-# myenv: foo
-# schedule: "0 0 * * *"
-# useOwnerReferencesInBackup: false
-# template:
-# ttl: "240h"
-# storageLocation: default
-# includedNamespaces:
-# - foo
-schedules: {}
-
-# Velero ConfigMaps.
-# Eg:
-# configMaps:
-# fs-restore-action-config:
-# labels:
-# velero.io/plugin-config: ""
-# velero.io/pod-volume-restore: RestoreItemAction
-# data:
-# image: velero/velero-restore-helper:v1.10.2
-configMaps: {}
-
-##
-## End of additional Velero resource settings.
-##
\ No newline at end of file
diff --git a/deploy/_deprecated/preload-runtime-go117.yaml b/deploy/_deprecated/preload-runtime-go117.yaml
deleted file mode 100644
index 7e46d43..0000000
--- a/deploy/_deprecated/preload-runtime-go117.yaml
+++ /dev/null
@@ -1,29 +0,0 @@
-# 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.
-#
----
-apiVersion: v1
-kind: Pod
-metadata:
- name: preload-runtime-go117
- namespace: nuvolaris
-spec:
- containers:
- - name: nodejs
- image: "openwhisk/action-golang-v1.17:nightly"
- command: ["/bin/true"]
- restartPolicy: Never
diff --git a/deploy/_deprecated/preload-runtime-nodej14.yaml b/deploy/_deprecated/preload-runtime-nodej14.yaml
deleted file mode 100644
index 2b7d99e..0000000
--- a/deploy/_deprecated/preload-runtime-nodej14.yaml
+++ /dev/null
@@ -1,28 +0,0 @@
-# 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.
-#
-apiVersion: v1
-kind: Pod
-metadata:
- name: preload-runtime-nodejs14
- namespace: nuvolaris
-spec:
- containers:
- - name: nodejs
- image: "openwhisk/action-nodejs-v14:nightly"
- command: ["/bin/true"]
- restartPolicy: Never
diff --git a/deploy/_deprecated/preload-runtime-python37.yaml b/deploy/_deprecated/preload-runtime-python37.yaml
deleted file mode 100644
index e357e1c..0000000
--- a/deploy/_deprecated/preload-runtime-python37.yaml
+++ /dev/null
@@ -1,29 +0,0 @@
-# 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.
-#
----
-apiVersion: v1
-kind: Pod
-metadata:
- name: preload-runtime-python37
- namespace: nuvolaris
-spec:
- containers:
- - name: nodejs
- image: "openwhisk/action-python-v3.7:nightly"
- command: ["/bin/true"]
- restartPolicy: Never
diff --git a/deploy/_deprecated/standalone-kcf.yaml b/deploy/_deprecated/standalone-kcf.yaml
deleted file mode 100644
index 3405800..0000000
--- a/deploy/_deprecated/standalone-kcf.yaml
+++ /dev/null
@@ -1,77 +0,0 @@
-#
-# 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.
-#
-apiVersion: v1
-kind: ConfigMap
-metadata:
- name: standalone-kcf
- namespace: nuvolaris
-data:
- standalone-kcf.conf: |
- include classpath("standalone.conf")
-
- whisk {
- spi {
- ContainerFactoryProvider = "org.apache.openwhisk.core.containerpool.kubernetes.KubernetesContainerFactoryProvider"
- LogStoreProvider = "org.apache.openwhisk.core.containerpool.logging.DockerToActivationLogStoreProvider"
- ArtifactStoreProvider = "org.apache.openwhisk.core.database.CouchDbStoreProvider"
- }
-
- couchdb {
- protocol = http # One of "https" or "http"
- host = {{couchdb_host}} # DB Host
- port = {{couchdb_port}} # DB Port
- username = "{{admin_user}}"
- password = "{{admin_password}}"
- provider = "CouchDB" # Either "Cloudant" or "CouchDB"
- databases { # Database names used for various entity classes
- WhiskAuth = "nuvolaris_subjects"
- WhiskEntity = "nuvolaris_whisks"
- WhiskActivation = "nuvolaris_activations"
- }
- }
-
- config {
- controller-instances = 1
- limits-triggers-fires-perMinute = {{triggers_fires_perMinute}}
- limits-actions-sequence-maxLength = {{actions_sequence_maxLength}}
- limits-actions-invokes-perMinute = {{actions_invokes_perMinute}}
- limits-actions-invokes-concurrent = {{actions_invokes_concurrent}}
- }
-
- api {
- host {
- proto = "http"
- port = "3233"
- name = "controller"
- }
- }
-
- kubernetes {
- timeouts {
- # Use higher timeout for run as in local dev the required Docker images may not be pre pulled
- run = 10 minute
- logs = 1 minute
- }
- user-pod-node-affinity {
- enabled = false
- }
- port-forwarding-enabled = false
- action-namespace = nuvolaris
- }
- helm.release = "release"
- runtime.delete.timeout = "30 seconds"
- }
diff --git a/deploy/_deprecated/standalone-lb.yaml b/deploy/_deprecated/standalone-lb.yaml
deleted file mode 100644
index bea7bd8..0000000
--- a/deploy/_deprecated/standalone-lb.yaml
+++ /dev/null
@@ -1,29 +0,0 @@
-# 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.
-#
-apiVersion: v1
-kind: Service
-metadata:
- namespace: nuvolaris
- name: apihost
-spec:
- type: LoadBalancer
- selector:
- app: controller
- ports:
- - port: 80
- targetPort: 3233
diff --git a/deploy/_deprecated/standalone-np.yaml b/deploy/_deprecated/standalone-np.yaml
deleted file mode 100644
index b00e9c4..0000000
--- a/deploy/_deprecated/standalone-np.yaml
+++ /dev/null
@@ -1,58 +0,0 @@
-# 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.
-#
-apiVersion: v1
-kind: Service
-metadata:
- namespace: nuvolaris
- name: controller
-spec:
- type: NodePort
- selector:
- app: controller
- ports:
- - port: 3233
- targetPort: 3233
- nodePort: 30233
-#---
-#apiVersion: v1
-#kind: Service
-#metadata:
-# namespace: nuvolaris
-# name: playground
-#spec:
-# type: NodePort
-# selector:
-# app: controller
-# ports:
-# - port: 3232
-# targetPort: 3232
-# nodePort: 30232
----
-apiVersion: v1
-kind: Service
-metadata:
- namespace: nuvolaris
- name: debugger
-spec:
- type: NodePort
- selector:
- app: controller
- ports:
- - port: 7896
- targetPort: 7896
- nodePort: 30896
diff --git a/deploy/_deprecated/standalone-set.yaml b/deploy/_deprecated/standalone-set.yaml
deleted file mode 100644
index 7a0e715..0000000
--- a/deploy/_deprecated/standalone-set.yaml
+++ /dev/null
@@ -1,61 +0,0 @@
-# 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.
-#
-apiVersion: apps/v1
-kind: StatefulSet
-metadata:
- namespace: nuvolaris
- name: controller
-spec:
- replicas: 1
- selector:
- matchLabels:
- app: controller
- serviceName: "controller"
- template:
- metadata:
- labels:
- app: controller
- spec:
- serviceAccountName: controller
- restartPolicy: Always
- containers:
- - name: openwhisk-standalone
- image: ghcr.io/nuvolaris/openwhisk-standalone:0.2.1-trinity.22062010
- imagePullPolicy: IfNotPresent
- ports:
- #- containerPort: 3232
- # name: playground
- - containerPort: 3233
- name: apihost
- - containerPort: 7896
- name: debugger
- lifecycle:
- preStop:
- exec:
- command: [
- "/home/bin/stop"
- ]
- volumeMounts:
- - name: standalone-kcf-vol
- mountPath: "/home/etc/standalone-kcf.conf"
- subPath: "standalone-kcf.conf"
- volumes:
- - name: standalone-kcf-vol
- configMap:
- name: standalone-kcf
-
diff --git a/deploy/_deprecated/standalone-test-in-https.yaml b/deploy/_deprecated/standalone-test-in-https.yaml
deleted file mode 100644
index c7b251b..0000000
--- a/deploy/_deprecated/standalone-test-in-https.yaml
+++ /dev/null
@@ -1,47 +0,0 @@
-# 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.
-#
-apiVersion: networking.k8s.io/v1
-kind: Ingress
-metadata:
- name: standalone
- namespace: nuvolaris
- annotations:
- nginx.ingress.kubernetes.io/proxy-body-size: "48m"
- nginx.ingress.kubernetes.io/proxy-connect-timeout: "600"
- nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
- nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
- nginx.ingress.kubernetes.io/ssl-redirect: "true"
- kubernetes.io/ingress.class: "{{ingress_class}}"
- cert-manager.io/cluster-issuer: "letsencrypt-issuer"
-
-spec:
- tls:
- - hosts:
- - linode.nuvolaris.io
- secretName: nuvolaris-letsencrypt-secret
- rules:
- - host: linode.nuvolaris.io
- http:
- paths:
- - path: /
- pathType: Prefix
- backend:
- service:
- name: controller
- port:
- number: 3233
diff --git a/enterprise-util/couchdb/couchdb-nuvolaris-app.yaml b/enterprise-util/couchdb/couchdb-nuvolaris-app.yaml
deleted file mode 100644
index b19fef1..0000000
--- a/enterprise-util/couchdb/couchdb-nuvolaris-app.yaml
+++ /dev/null
@@ -1,42 +0,0 @@
-# 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.
-#
-
-apiVersion: networking.k8s.io/v1
-kind: Ingress
-metadata:
- annotations:
- cert-manager.io/cluster-issuer: letsencrypt-issuer
- kubernetes.io/ingress.class: traefik
- name: couchdb
- namespace: nuvolaris
-spec:
- rules:
- - host: couchdb.nuvolaris.app
- http:
- paths:
- - backend:
- service:
- name: couchdb
- port:
- number: 5984
- path: /
- pathType: Prefix
- tls:
- - hosts:
- - couchdb.nuvolaris.app
- secretName: couchdb-letsencrypt-secret
diff --git a/enterprise-util/couchdb/couchdb-nuvolaris-org-ingress.yaml b/enterprise-util/couchdb/couchdb-nuvolaris-org-ingress.yaml
deleted file mode 100644
index 528d64b..0000000
--- a/enterprise-util/couchdb/couchdb-nuvolaris-org-ingress.yaml
+++ /dev/null
@@ -1,42 +0,0 @@
-# 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.
-#
-
-apiVersion: networking.k8s.io/v1
-kind: Ingress
-metadata:
- annotations:
- cert-manager.io/cluster-issuer: letsencrypt-issuer
- kubernetes.io/ingress.class: traefik
- name: couchdb
- namespace: nuvolaris
-spec:
- rules:
- - host: couchdb.nuvolaris.org
- http:
- paths:
- - backend:
- service:
- name: couchdb
- port:
- number: 5984
- path: /
- pathType: Prefix
- tls:
- - hosts:
- - couchdb.nuvolaris.org
- secretName: couchdb-letsencrypt-secret
diff --git a/enterprise-util/minio/minio-aks-ingress.yaml b/enterprise-util/minio/minio-aks-ingress.yaml
deleted file mode 100644
index b0857cc..0000000
--- a/enterprise-util/minio/minio-aks-ingress.yaml
+++ /dev/null
@@ -1,42 +0,0 @@
-# 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.
-#
-
-apiVersion: networking.k8s.io/v1
-kind: Ingress
-metadata:
- annotations:
- #cert-manager.io/cluster-issuer: letsencrypt-issuer
- kubernetes.io/ingress.class: nginx
- name: minio
- namespace: nuvolaris
-spec:
- rules:
- - host: minio.20.232.221.33.nip.io
- http:
- paths:
- - backend:
- service:
- name: minio
- port:
- number: 9090
- path: /
- pathType: Prefix
- #tls:
- #- hosts:
- # - minio.nuvolaris.org
- # secretName: minio-letsencrypt-secret
diff --git a/enterprise-util/minio/minio-nuvolaris-org-ingress.yaml b/enterprise-util/minio/minio-nuvolaris-org-ingress.yaml
deleted file mode 100644
index 09b86bd..0000000
--- a/enterprise-util/minio/minio-nuvolaris-org-ingress.yaml
+++ /dev/null
@@ -1,42 +0,0 @@
-# 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.
-#
-
-apiVersion: networking.k8s.io/v1
-kind: Ingress
-metadata:
- annotations:
- cert-manager.io/cluster-issuer: letsencrypt-issuer
- kubernetes.io/ingress.class: traefik
- name: prometheus
- namespace: nuvolaris
-spec:
- rules:
- - host: minio.nuvolaris.org
- http:
- paths:
- - backend:
- service:
- name: minio
- port:
- number: 9090
- path: /
- pathType: Prefix
- tls:
- - hosts:
- - minio.nuvolaris.org
- secretName: minio-letsencrypt-secret
diff --git a/enterprise-util/minio/minio-saa1-ingress.yaml b/enterprise-util/minio/minio-saa1-ingress.yaml
deleted file mode 100644
index ddb897d..0000000
--- a/enterprise-util/minio/minio-saa1-ingress.yaml
+++ /dev/null
@@ -1,37 +0,0 @@
-# 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.
-#
-
-apiVersion: networking.k8s.io/v1
-kind: Ingress
-metadata:
- annotations:
- kubernetes.io/ingress.class: traefik
- name: minio
- namespace: nuvolaris
-spec:
- rules:
- - host: minio.146.148.119.191.nip.io
- http:
- paths:
- - backend:
- service:
- name: minio
- port:
- number: 9090
- path: /
- pathType: Prefix
diff --git a/enterprise-util/monitoring/prometheus-localhost.yaml b/enterprise-util/monitoring/prometheus-localhost.yaml
deleted file mode 100644
index b3e81af..0000000
--- a/enterprise-util/monitoring/prometheus-localhost.yaml
+++ /dev/null
@@ -1,37 +0,0 @@
-# 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.
-#
-
-apiVersion: networking.k8s.io/v1
-kind: Ingress
-metadata:
- annotations:
- kubernetes.io/ingress.class: nginx
- name: prometheus
- namespace: nuvolaris
-spec:
- rules:
- - host: localhost
- http:
- paths:
- - backend:
- service:
- name: nuvolaris-prometheus-server
- port:
- number: 9999
- path: /monitoring
- pathType: Prefix
diff --git a/enterprise-util/monitoring/prometheus-mistergpt-app-ingress.yaml b/enterprise-util/monitoring/prometheus-mistergpt-app-ingress.yaml
deleted file mode 100644
index 77f8f79..0000000
--- a/enterprise-util/monitoring/prometheus-mistergpt-app-ingress.yaml
+++ /dev/null
@@ -1,37 +0,0 @@
-# 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.
-#
-
-apiVersion: networking.k8s.io/v1
-kind: Ingress
-metadata:
- annotations:
- kubernetes.io/ingress.class: traefik
- name: prometheus
- namespace: nuvolaris
-spec:
- rules:
- - host: monitoring.mistergpt.app
- http:
- paths:
- - backend:
- service:
- name: nuvolaris-prometheus-server
- port:
- number: 9999
- path: /
- pathType: Prefix
diff --git a/enterprise-util/monitoring/prometheus-nuvolaris-app-ingress.yaml b/enterprise-util/monitoring/prometheus-nuvolaris-app-ingress.yaml
deleted file mode 100644
index 71afbea..0000000
--- a/enterprise-util/monitoring/prometheus-nuvolaris-app-ingress.yaml
+++ /dev/null
@@ -1,42 +0,0 @@
-# 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.
-#
-
-apiVersion: networking.k8s.io/v1
-kind: Ingress
-metadata:
- annotations:
- cert-manager.io/cluster-issuer: letsencrypt-issuer
- kubernetes.io/ingress.class: traefik
- name: prometheus
- namespace: nuvolaris
-spec:
- rules:
- - host: monitoring.nuvolaris.app
- http:
- paths:
- - backend:
- service:
- name: nuvolaris-prometheus-server
- port:
- number: 9999
- path: /
- pathType: Prefix
- tls:
- - hosts:
- - monitoring.nuvolaris.app
- secretName: monitoring-letsencrypt-secret
diff --git a/enterprise-util/monitoring/prometheus-nuvolaris-org-ingress.yaml b/enterprise-util/monitoring/prometheus-nuvolaris-org-ingress.yaml
deleted file mode 100644
index 9fdf13b..0000000
--- a/enterprise-util/monitoring/prometheus-nuvolaris-org-ingress.yaml
+++ /dev/null
@@ -1,42 +0,0 @@
-# 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.
-#
-
-apiVersion: networking.k8s.io/v1
-kind: Ingress
-metadata:
- annotations:
- cert-manager.io/cluster-issuer: letsencrypt-issuer
- kubernetes.io/ingress.class: traefik
- name: prometheus
- namespace: nuvolaris
-spec:
- rules:
- - host: monitoring.nuvolaris.org
- http:
- paths:
- - backend:
- service:
- name: nuvolaris-prometheus-server
- port:
- number: 9999
- path: /
- pathType: Prefix
- tls:
- - hosts:
- - monitoring.nuvolaris.org
- secretName: monitoring-letsencrypt-secret
diff --git a/enterprise/.gitkeep b/enterprise/.gitkeep
deleted file mode 100644
index e69de29..0000000
(openserverless-operator) 18/24: fix: update to licenserc file
Posted by fr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
franztt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openserverless-operator.git
commit 7b8f1c128095715d3cd3554cfcf190948a2791ca
Author: francescotimperi <fr...@gmail.com>
AuthorDate: Wed Jul 24 17:39:18 2024 +0000
fix: update to licenserc file
---
.licenserc.yaml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/.licenserc.yaml b/.licenserc.yaml
index ae878bd..6d89a65 100644
--- a/.licenserc.yaml
+++ b/.licenserc.yaml
@@ -29,6 +29,8 @@ header:
- 'nodesource_setup.sh'
- '**/pdm.lock'
- '**/LICENSE'
+ - '**/DISCLAIMER'
+ - '**/NOTICE'
- '**/package-lock.json'
- '**/.*-version'
- '**/*.json'
@@ -54,6 +56,7 @@ header:
- 'nuv/**'
- 'olaris/**'
- 'skip-test'
+ - '.env.dist'
dependency:
(openserverless-operator) 19/24: chore: added TAG extraction steps
Posted by fr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
franztt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openserverless-operator.git
commit 29a4251588738e3f7ab3f05068c873a33a284e0e
Author: francescotimperi <fr...@gmail.com>
AuthorDate: Wed Jul 24 18:24:24 2024 +0000
chore: added TAG extraction steps
---
.github/workflows/image.yml | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/image.yml b/.github/workflows/image.yml
index 5157287..2af8c8a 100644
--- a/.github/workflows/image.yml
+++ b/.github/workflows/image.yml
@@ -37,6 +37,8 @@ on:
- '[0-9]*'
branches-ignore:
- '*'
+ pull_request:
+ branches: [main]
jobs:
image:
@@ -52,7 +54,15 @@ jobs:
with:
submodules: recursive
- name: License
- uses: apache/skywalking-eyes@main
+ uses: apache/skywalking-eyes@main
+ - name: Assign TAG from pull request
+ if: ${{ github.event_name == 'pull_request' }}
+ run: |
+ echo "IMG_TAG=$(git rev-parse --short HEAD)" >> "$GITHUB_ENV"
+ - name: Assign TAG from push
+ if: ${{ github.event_name != 'pull_request' }}
+ run: |
+ echo "IMG_TAG=$(git describe --tags --abbrev=0)" >> "$GITHUB_ENV"
- name: Registry login
uses: docker/login-action@v3
with:
@@ -68,4 +78,4 @@ jobs:
with:
version: latest
- name: Image
- run: task buildx-and-push
\ No newline at end of file
+ run: task buildx-and-push TAG="$IMG_TAG"
\ No newline at end of file
(openserverless-operator) 07/24: chore: task all description update
Posted by fr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
franztt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openserverless-operator.git
commit 9fc799b3f4efc3d73f85f12ccb45e1f838b61bf3
Author: francescotimperi <fr...@gmail.com>
AuthorDate: Sat Jul 13 15:29:09 2024 +0000
chore: task all description update
---
README.md | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 08d7ca9..b76c350 100644
--- a/README.md
+++ b/README.md
@@ -65,11 +65,12 @@ git branch -u https://github.com/<GITHUB_USERNAME>/openserverless-operator
That's it. Now you can use `task build` to build the image
7. Deploy the operator
-Execute the command
+
+To deploy a testing configuration of the OpenServerless operator execute the command
```shell
task all
```
-it will deploy the openserverless-operator applying `/test/k3s/whisk.yaml` which will deploy a complete configuration but TLS and MONITORING.
+The operator instance will be configured applying the `test/k3s/whisk.yaml` template. All the components are activated except TLS and MONITORING.
(openserverless-operator) 14/24: chore: updated version tag and added DISCLAIMER and NOTICE file for incubating ASF projects
Posted by fr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
franztt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openserverless-operator.git
commit a705f511226e9b758d1351e94303fe6e4f9de9a7
Author: francescotimperi <fr...@gmail.com>
AuthorDate: Tue Jul 23 15:51:15 2024 +0000
chore: updated version tag and added DISCLAIMER and NOTICE file for incubating ASF projects
---
DISCLAIMER | 11 +++++++++++
NOTICE | 6 ++++++
Taskfile.yml | 2 +-
3 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/DISCLAIMER b/DISCLAIMER
new file mode 100644
index 0000000..b505beb
--- /dev/null
+++ b/DISCLAIMER
@@ -0,0 +1,11 @@
+Apache OpenServerless (Incubating) is an effort undergoing incubation at the Apache
+Software Foundation (ASF), sponsored by the Apache Incubator PMC.
+
+Incubation is required of all newly accepted projects until a further review
+indicates that the infrastructure, communications, and decision making process
+have stabilized in a manner consistent with other successful ASF projects.
+
+While incubation status is not necessarily a reflection of the completeness
+or stability of the code, it does indicate that the project has yet to be
+fully endorsed by the ASF.
+
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..9b09468
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,6 @@
+Apache OpenServerless (Incubating)
+Copyright 2024 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
diff --git a/Taskfile.yml b/Taskfile.yml
index 7045b0f..5d4ed56 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -22,7 +22,7 @@ vars:
N: ""
T: ""
P: ""
- BASETAG: 0.1-dev
+ BASETAG: 0.1-snapshot
KUBE:
sh: ./detect.sh
NS: "nuvolaris"
(openserverless-operator) 05/24: fix: update kaniko.yaml to properly build openserverless operator
Posted by fr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
franztt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openserverless-operator.git
commit da9b3549537eaafc29c2778dcc82cbe8663e069e
Author: francescotimperi <fr...@gmail.com>
AuthorDate: Sat Jul 13 13:21:25 2024 +0000
fix: update kaniko.yaml to properly build openserverless operator
---
.env.dist | 10 +-
README.md | 187 ++++-----------------
README.md => README_old.md | 0
Taskfile.yml | 70 ++++++--
.../traefik-middleware.yaml => kaniko-build.yaml | 26 ++-
kaniko.yaml | 26 ---
...traefik-middleware.yaml => operator-deploy.yaml | 13 +-
.../k3s/traefik-middleware.yaml => registries.yaml | 12 +-
shell.nix | 1 +
tests/k3s/{whisk.yaml => whisk-full.yaml} | 0
tests/k3s/whisk-mysc2.yaml | 123 --------------
tests/k3s/whisk-saas.yaml | 174 -------------------
tests/k3s/whisk.yaml | 2 +-
13 files changed, 131 insertions(+), 513 deletions(-)
diff --git a/.env.dist b/.env.dist
index 072d08a..0e6a017 100644
--- a/.env.dist
+++ b/.env.dist
@@ -1 +1,9 @@
-GITHUB_USER=github: put here your github user
\ No newline at end of file
+GITHUB_USER=github: put here your github user
+MY_OPERATOR_IMAGE=ghcr.io/${GITHUB_USER}/openserverless-operator
+
+#enterprise monitoring
+SLACK_API_URL=your-slack-webhook-url
+GMAIL_USERNAME=your-gmail-username
+GMAIL_PASSWORD=your-gmail-app-password
+ALERT_EMAIL_FROM=your-gmail-from-account
+ALERT_EMAIL_TO=your-email-to-comma-separated-addresses
\ No newline at end of file
diff --git a/README.md b/README.md
index 65f7e97..08d7ca9 100644
--- a/README.md
+++ b/README.md
@@ -17,188 +17,59 @@
~ under the License.
~
-->
-# Apache OpenServerless Operator
+# Apache OpenServerless Operator
-This is the Kubernetes Operator of the [bit.ly/nuvolaris](Apache OpenServerless project).
+In this readme there are informations for developers.
-If you are interested in developing it, please read the [design document](DESIGN.md). Please also read the [development environment document](https://github.com/nuvolaris/nuvolaris/blob/main/docs/DEVEL.md) to learn how to setup it.
+We describe how to build and test the operator in our development environment
-If you wonder what are the strange subfolders, here is an explanation:
+Please refer to the [website](https://openserverless.apache.org) for user informations.
-- `nuvolaris` are python modules, and implements the operator logic, coded in Python.
+## How to build and use an operator image
-So you generally build the `nuvolaris` operator as a docker image and publishi, build the `nuv` CLI and use the `olaris` tasks to install and manage it...
+Ensure you have satified the prerequisites below. Most notably, you need to use our development virtual machine and you need write access to a github repository repository.
-## Developer notes
+Once you have satisfied the prerequisites, you can build an image you can use in the development machine.
-The operator is built in Python with [kopf](https://kopf.readthedocs.io/en/stable/). You can find some [examples here](https://github.com/nolar/kopf/tree/main/examples).
+Build an image with `task build`.
-The operator uses under the hood [kustomize](https://kustomize.io/) and [kubectl](https://kubernetes.io/docs/reference/kubectl/) to interact with Kubernetes.
+Please note the it will build the image locally and push in an internal registry, even if it is name is `ghcr.io/${GITHUB_USER}/openserverless-operator`.
-Let's discuss how to:
+To be able to build, the task `build` will commit and push all your changes and then build the operator from the public sources in your local k3s.
-- Select the Kubernetes cluster to use
-- Develop interactively (cli and tests)
-- Run it without deploying
-- Test it in a local cluster without pushing an image to a registry
-- Push it to a public registry to use with external Kubernetes
+It will also generate
-## Selecting the Kubernetes Clusters
+You can then deploy it with `task deploy`.
-You have to run the operator against multiple clusters for test. So the operator has a provision to selection one to use and work with.
+Once you have finished with development you can create a publici image with `task publish` that will publish the tag and trigger a creation of the image.
-You can use other clusters for testing and development. You should place all the configurations for the other clusters in the folder `clusters`, with extension `.kubeconfig`. See [below](#creating-a-new-cluster) for informations about this.
+## Prerequisites
-If you use the development environment, using Docker and VSCode, you have available a Kind cluster ready for test. Use the task `task kind:config` to copy the configuration in the `clusters` folder if you plan to change cluster.
+1. Please setup and use a development VM [as described here](https://github.com/apache/openserverless)
-In order to test the operator against different clusters, you can list all the cluster configurations available with `task` and then select one with `task N` where N is the number of the configuration in the list shown.
+2. With VSCode, access to the development VM, open the workspace `openserverless/openserverless.code-orkspace` and then open a terminal with `operator` subproject e enabling the nix enviroment with direnv (the vm provides those).
-Example:
+3. Create a fork of `githbub.com/apache/openserverless-operator`
-```
-$ task
-*** current: kind
-1 clusters/aks.kubeconfig
-2 clusters/eks.kubeconfig
-3 clusters/kind.kubeconfig
-4 clusters/microk8s.kubeconfig
-*** select with 'task #'
-$ task 2
-task: [2] task use N=2
-cluster: eks
-Kubeconfig user entry is using deprecated API version client.authentication.k8s.io/v1alpha1. Run 'aws eks update-kubeconfig' to update.
-NAME STATUS ROLES AGE VERSION
-ip-192-168-42-140.eu-central-1.compute.internal Ready <none> 49d v1.22.6-eks-7d68063
-```
-
-## Developing the operator interactively
-
-Start coding with `task cli`.
-
-You can then develop some code experimentally interacting with a python interpreter with the same libraries and some useful imports and configuration ready (most notably the autoreload).
-
-You can then execute some tests.
-
-- `task utest`: unit tests
-- `task itest`: integration tests
-
-This will run all the tests. You can select which test to run with `tast itest T=xxx` or `task utest T=xxx`. It will then execute all the tests whose file starts with `xxx`.
-
-## Developing the operator without deploying it
-
-You can then start the whole operator without having to deploy it. Start it with `task run`.
-
-Once you started the operarto pen another terminal and Use `task instance` to apply a crd instance depending on the current clusters.
-
-The crd instance used it is defined by the type of kubernetes custer you are using. The script './detect.sh' tries to detect it. The configuration used is under `tests/<detected>/whisk.yaml`
-
-If you have not changed the cluster the default cluster is 'kind' so it will apply the configuration `tests/kind/whisk.yaml`
-
-You can change the configuration passing the `WHISK` parameter (without the .yaml). So if you are running an `eks` cluster and you want to use your configuration `custom` the command `task instance WHISK=custom` will use `tests/eks/custom.yaml`
-
-Other useful tasks:
-
-- `task watch` starts watching deployed pods servics and nodes - useful to see what happens when you deploy
-- `task destroy` removes the instance but not the operator so you can recreate the cluster with `task instance` maybe with different parameters
-- `task clean` removes everything including the operator and pvc
-- If something get stuck and the cleanup does not complete `task defin` can help removing incomplete finalizers (you need to open another terminal to run it)
-
-## Testing the operator deploying it locally
-
-First and before all, you need a tag.
-
-Execute `task image-tag`. This command will delete all the other tag and generate a tag in the format `x.y.z-milestone.timestamp`. This tag is essential to identify your operator.
-
-When you are confident you can try the operator, in kind, use `task build-and-load`. This will build an instance and load it in the local kind. This step does not require you publish it to a public registry.
-
-You can then proceed deploying it with `task operator` to deploy it.
-
-You can then use `task instance` to deploy a crd instance, and finally `task actions` to test whisk running a few actions
+4. Copy .env.dist in .env and put your github username in it
-## Publishing the operator
-
-Once the operator is ready, you can build and test it a against a kubernetes cluster.
-
-First, generate a new image tag with `task image-tag`.
-
-You can test locally using the kind cluster (provided by default by the development environment) with `build-and-load`.
-
-To test it against other clusters, you need to publish it to a public repository.
-
-If you have push access to Nuvolaris repository just push the tag and it will trigger the build using the tag to tag the repository and it will be published in the nuvolaris package registry on GitHub.
-
-You can also publish an image to your own github repository for testing purposes. To do this, add to your `.env` the following variables:
+5. Since the build requires you push your sources in your repo, you need the credentials to access it. The fastest way is to [create a personal token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens)
+6. Now setup a remote to access your repo and set it as your default upstream branch.
```
-MY_CONTROLLER_IMAGE=<your-user>/<your-name>
-GITHUB_USER=<your-user>
-GITHUB_TOKEN=<your-access-token>
+git remote add fork https://<GITHUB_USERNAME>:<GITHUB_TOKEN>@github.com/<GITHUB_USERNAME>/openserverless-operator
+git branch -u https://github.com/<GITHUB_USERNAME>/openserverless-operator
```
-You cannot override the tag of the operator you have to generate a git tag: `task image-tag`. Note the tag is unique for the current hour (it embeds: YYMMDDhh).
-
-If you set those variables you can use
-- `task b:docker-login` to log to the current docker registry
-- `task b:build-and-push` to build for one single architecture (faster but limited to your architecture)
-- `task buildx-and-push` to build for all the architectues (slower, used by the GitHub action)
-
-Remember that for GitHub Containter Registry you have to make public the image you are using to tlet it to be accessible by other Kubernetes
-
-### Running tests
-
-Once you have choosen the Kubernetes cluster and published the images on a public registry you can test it with `task dtest`, that will run a complete deployment in the current Kubernetes cluster.
-
-If you want to run all the tests (u, i, d) aginst the current cluster use `task test`
+That's it. Now you can use `task build` to build the image
-Finally if you want to run the tests against all the configured kubernetes use `task all-kubes -- <target>`.
+7. Deploy the operator
+Execute the command
-This targes runs a group of tests against all the available clusters, so you can run all the tests against all the kubes with `task all-kubes -- test`
-
-# Reference
-
-- `use` (default task) lists all the clusters;
-- `1`, `2`...`9` selects the corresponding entry
-- `operator`: deploys the operator in the current cluster.
-- `instance`: deploy an instance of the configuration to build an actual cluster.
-- `destroy`: destroy the current deployment
-- `clean`: remove everything - if you get stuck use `defin`
-- `defin`: remove finalizer for the controller, useful if `clean` does not complete
-- `config`: once it is deployed, extracts the current configuration to use `wsk`
-- `actions`: deploy and run test actions
-- `utest` runs unit tests, filter with `T=<xxx>`
-- `itest` runs integration tests, filter with `T=<xxx>`
-- `iclean`: remote temp files generated by the tests
-- `dtest` deployment test
-- `hello`: runs a simple hello world test
-- `ping`: runs a ping test of redis
-- `all-kubes -- <target>`: execute the `<target>` against all the configured kubes
-
-Build targets
-
-- `b:buildx-and-push`: build and push the operator in multiple architectectures (slow)
-- `b:build-and-load`: build and load the operator in the kind cluster
-- `b:docker-login`: login into the github docker registry
-- `b:build-and-push`: build and push the operator in the current architecture
-
-Currently the following clusters, with respective suffix are supported (note not all the clusters are fully implemented, and you may need to edit and set manually variables - see the Taskfiles for reference).
-
-- Kubernetes Kind: `kind`
-- Amazon EKS: `eks`
-- Azure AKS: `aks`
-- Google GKE: `gke`
-- Ubuntu MicroK8s: `m8s`
-- Rancher K3S: `k3s`
-- RedHat OpenShift: `osh`
-
-See below for creating clusters and configurations. All the configuration for the available clusters are expected to be in `clusters/*.kubeconfig`
-
-Available commands for the cluster `xxx` (not always and not for all the cases):
-
-- `xxx:list`: list existing clusters
-- `xxx:create`: create a test cluster
-- `xxx:destroy`: destroy a test cluster
-- `xxx:config`: set the kubeconfig to the current cluster
+```shell
+task all
+```
-Note that `kind` is available in the development environment by defaut. You may want to use `task kind:config` to extract the configuration to be able to switch back to the local clusters.
+it will deploy the openserverless-operator applying `/test/k3s/whisk.yaml` which will deploy a complete configuration but TLS and MONITORING.
diff --git a/README.md b/README_old.md
similarity index 100%
copy from README.md
copy to README_old.md
diff --git a/Taskfile.yml b/Taskfile.yml
index 89ddda4..7045b0f 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -28,7 +28,6 @@ vars:
NS: "nuvolaris"
PREFIX: ""
TAG:
- sh: git describe --tags --abbrev=0 2>/dev/null || echo latest
dotenv:
- .env
@@ -170,6 +169,7 @@ tasks:
defin:
- task: d:defin
+ # old., to be removed
image-tag:
- git tag -d $(git tag)
- git tag -f {{.P}}{{.BASETAG}}.$(date +%y%m%d%H%M)
@@ -328,21 +328,71 @@ tasks:
docker-login:
- task: b:docker-login
+ # openserverless-operator sction
+ tag:
+ silent: true
+ desc: generate a new tag based on the current time
+ cmds:
+ - git tag -d $(git tag) || true
+ - git tag -f {{.BASETAG}}.$(date +%y%m%d%H%M)
+ - env PAGER= git tag
+
install-registry:
+ desc: install a local registry
+ cmds:
+ - sudo cp registries.yaml /etc/rancher/k3s/registries.yaml
+ - sudo systemctl restart k3s
- helm repo add twuni https://helm.twun.io
- - helm install docker-registry twuni/docker-registry --namespace default
+ - >
+ helm install docker-registry twuni/docker-registry
+ --namespace kube-system
+ --set image.tag=2.8.3
+ --set service.type=NodePort
+ --set service.nodePort=30050
+ status:
+ - kubectl -n kube-system get po -l app=docker-registry | grep docker-registry
- build:
+ tag-commit-push:
+ desc: tag, commit and push to your default upstream repo (see README.md)
+ cmds:
+ - task: tag
+ - git commit -m "{{.TAG}}" -a || true
+ - git push || true
+
+ kaniko-build:
+ desc: build in current kubernetes with Kaniko
env:
- TAG: "{{.TAG}}"
+ TAG:
+ sh: git describe --tags --abbrev=0 2>/dev/null || echo latest
cmds:
- - kubectl delete pod/kaniko
- - kubectl delete cm/dockerfile-configmap
- test -n "$GITHUB_USER" || true "did you configure your .env?"
- - envsubst <kaniko.yaml >_kaniko.yaml
- - cat _kaniko.yaml
- - kubectl -n default create configmap dockerfile-configmap --from-file=Dockerfile
- - kubectl apply -f _kaniko.yaml
+ - envsubst <kaniko-build.yaml >_kaniko-build.yaml
+ - kubectl -n default delete job/kaniko-build || true
+ - kubectl -n default apply -f _kaniko-build.yaml
+ - kubectl wait po --for=condition=ready -l app=kaniko-build -n default
+
+ build-logs:
+ desc: show logs of the latest build
+ cmds:
+ - kubectl -n default logs -l app=kaniko-build -f
+
+ build:
+ desc: build the operator image
+ cmds:
+ - task: install-registry
+ - task: tag-commit-push
+ - task: kaniko-build
+ - task: build-logs
+
+ deploy:
+ desc: deploy the current operator image
+ env:
+ TAG:
+ sh: git describe --tags --abbrev=0 2>/dev/null || echo latest
+ cmds:
+ - envsubst <operator-deploy.yaml >_operator-deploy.yaml
+ - kubectl apply -f _operator-deploy.yaml
+
shell:
- kubectl -n nuvolaris exec --stdin --tty {{.POD}} -- /bin/bash
diff --git a/tests/k3s/traefik-middleware.yaml b/kaniko-build.yaml
similarity index 53%
copy from tests/k3s/traefik-middleware.yaml
copy to kaniko-build.yaml
index 20c56a0..2e7dda6 100644
--- a/tests/k3s/traefik-middleware.yaml
+++ b/kaniko-build.yaml
@@ -15,11 +15,25 @@
# specific language governing permissions and limitations
# under the License.
#
-apiVersion: traefik.containo.us/v1alpha1
-kind: Middleware
+apiVersion: batch/v1
+kind: Job
metadata:
- namespace: nuvolaris
- name: franztt-static-ingress-add-prefix
+ name: kaniko-build
+ namespace: default
spec:
- addPrefix:
- prefix: /franztt-web
\ No newline at end of file
+ template:
+ metadata:
+ labels:
+ app: kaniko-build
+ spec:
+ containers:
+ - name: kaniko
+ image: gcr.io/kaniko-project/executor:latest
+ args:
+ - --context=git://github.com/${GITHUB_USER}/openserverless-operator
+ - --destination=docker-registry.kube-system.svc.cluster.local:5000/${GITHUB_USER}/openserverless-operator:${TAG}
+ - --insecure
+ - --insecure-registry=docker-registry
+ - --build-arg=OPERATOR_IMAGE_DEFAULT=ghcr.io/${GITHUB_USER}/openserverless-operator
+ - --build-arg=OPERATOR_TAG_DEFAULT=${TAG}
+ restartPolicy: Never
diff --git a/kaniko.yaml b/kaniko.yaml
deleted file mode 100644
index eeac3f4..0000000
--- a/kaniko.yaml
+++ /dev/null
@@ -1,26 +0,0 @@
-apiVersion: v1
-kind: Pod
-metadata:
- name: kaniko
- namespace: default
-spec:
- containers:
- - name: kaniko
- #image: busybox
- #command:
- #- sleep
- #- inf
- image: gcr.io/kaniko-project/executor:latest
- args:
- - --context=git://github.com/${GITHUB_USER}/openserverless-operator
- - --destination=docker-registry:5000/openserverless/operator:${TAG}
- - --insecure
- - --insecure-registry=docker-registry
- volumeMounts:
- - name: config-volume
- mountPath: /context
- restartPolicy: Never
- volumes:
- - name: config-volume
- configMap:
- name: dockerfile-configmap
diff --git a/tests/k3s/traefik-middleware.yaml b/operator-deploy.yaml
similarity index 82%
copy from tests/k3s/traefik-middleware.yaml
copy to operator-deploy.yaml
index 20c56a0..e3e965b 100644
--- a/tests/k3s/traefik-middleware.yaml
+++ b/operator-deploy.yaml
@@ -15,11 +15,12 @@
# specific language governing permissions and limitations
# under the License.
#
-apiVersion: traefik.containo.us/v1alpha1
-kind: Middleware
+apiVersion: v1
+kind: Pod
metadata:
- namespace: nuvolaris
- name: franztt-static-ingress-add-prefix
+ name: dummy-operator
+ namespace: default
spec:
- addPrefix:
- prefix: /franztt-web
\ No newline at end of file
+ containers:
+ - name: dummy-operator
+ image: ghcr.io/$GITHUB_USER/openserverless-operator:$TAG
\ No newline at end of file
diff --git a/tests/k3s/traefik-middleware.yaml b/registries.yaml
similarity index 81%
rename from tests/k3s/traefik-middleware.yaml
rename to registries.yaml
index 20c56a0..c0b47c4 100644
--- a/tests/k3s/traefik-middleware.yaml
+++ b/registries.yaml
@@ -15,11 +15,7 @@
# specific language governing permissions and limitations
# under the License.
#
-apiVersion: traefik.containo.us/v1alpha1
-kind: Middleware
-metadata:
- namespace: nuvolaris
- name: franztt-static-ingress-add-prefix
-spec:
- addPrefix:
- prefix: /franztt-web
\ No newline at end of file
+mirrors:
+ ghcr.io:
+ endpoint:
+ - "http://127.0.0.1:30050"
\ No newline at end of file
diff --git a/shell.nix b/shell.nix
index 537facb..3d4487b 100644
--- a/shell.nix
+++ b/shell.nix
@@ -28,6 +28,7 @@ pkgs.mkShellNoCC {
poetry
zip
unzip
+ # minio-client-2023-03-23T20-03-04Z
];
}
diff --git a/tests/k3s/whisk.yaml b/tests/k3s/whisk-full.yaml
similarity index 100%
copy from tests/k3s/whisk.yaml
copy to tests/k3s/whisk-full.yaml
diff --git a/tests/k3s/whisk-mysc2.yaml b/tests/k3s/whisk-mysc2.yaml
deleted file mode 100644
index 5bb1330..0000000
--- a/tests/k3s/whisk-mysc2.yaml
+++ /dev/null
@@ -1,123 +0,0 @@
-# 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.
-#
-apiVersion: nuvolaris.org/v1
-kind: Whisk
-metadata:
- name: controller
- namespace: nuvolaris
-spec:
- nuvolaris:
- password: nuvpassw0rd
- apihost: arcosicilia.org
- affinity: true
- tolerations: true
- protocol: https
- components:
- # start openwhisk controller
- openwhisk: true
- # start openwhisk invoker
- invoker: false
- # start couchdb
- couchdb: true
- # start kafka
- kafka: false
- # start mongodb
- mongodb: true
- # start redis
- redis: true
- # start cron based action parser
- cron: true
- # enable TLS
- tls: false
- # minio enabled or not
- minio: true
- # minio static enabled or not
- static: true
- # postgres enabled or not
- postgres: true
- openwhisk:
- namespaces:
- whisk-system: 789c46b1-71f6-4ed5-8c54-816aa4f8c502:abcfO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP
- nuvolaris: cbd68075-dac2-475e-8c07-d62a30c7e683:123zO3xKCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP
- couchdb:
- host: couchdb
- port: 5984
- volume-size: 10
- admin:
- user: whisk_admin
- password: some_passw0rd
- controller:
- user: invoker_admin
- password: s0meP@ass1
- invoker:
- user: controller_admin
- password: s0meP@ass2
- kafka:
- host: kafka
- volume-size: 10
- scheduler:
- schedule: "* * * * *"
- tls:
- acme-registered-email: francesco@nuvolaris.io
- acme-server-url: https://acme-v02.api.letsencrypt.org/directory
- configs:
- limits:
- actions:
- sequence-maxLength: 50
- invokes-perMinute: 999
- invokes-concurrent: 250
- triggers:
- fires-perMinute: 999
- controller:
- javaOpts: "-Xmx2048M"
- redis:
- persistence-enabled: true
- volume-size: 10
- default:
- password: s0meP@ass3
- nuvolaris:
- prefix: nuv
- password: s0meP@ass3
- mongodb:
- host: mongodb
- volume-size: 10
- admin:
- user: whisk_admin
- password: 0therPa55
- nuvolaris:
- user: nuvolaris
- password: s0meP@ass3
- exposedExternally: False
- useOperator: False
- minio:
- volume-size: 2
- admin:
- user: minioadmin
- password: minioadmin
- nuvolaris:
- user: nuvolaris
- password: zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG
- postgres:
- volume-size: 5
- replicas: 2
- admin:
- password: 0therPa55
- replica-password: 0therPa55RR
- nuvolaris:
- password: s0meP@ass3
-
diff --git a/tests/k3s/whisk-saas.yaml b/tests/k3s/whisk-saas.yaml
deleted file mode 100644
index 4231b5d..0000000
--- a/tests/k3s/whisk-saas.yaml
+++ /dev/null
@@ -1,174 +0,0 @@
-# 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.
-#
-apiVersion: nuvolaris.org/v1
-kind: Whisk
-metadata:
- name: controller
- namespace: nuvolaris
-spec:
- nuvolaris:
- password: nuvpassw0rd
- apihost: 146.148.119.191
- storageclass: rook-ceph-block
- provisioner: rook-ceph.rbd.csi.ceph.com
- components:
- # start openwhisk controller
- openwhisk: true
- # start openwhisk invoker
- invoker: false
- # start couchdb
- couchdb: true
- # start kafka
- kafka: false
- # start mongodb
- mongodb: true
- # start redis
- redis: true
- # start cron based action parser
- cron: true
- # enable TLS
- tls: false
- # minio enabled or not
- minio: true
- # zookeeper enabled or not
- zookeeper: false
- # minio static enabled or not
- static: true
- # monitoring enabled or not
- monitoring: false
- # postgres enabled or not
- postgres: true
- openwhisk:
- namespaces:
- whisk-system: 789c46b1-71f6-4ed5-8c54-816aa4f8c502:abcfO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP
- nuvolaris: cbd68075-dac2-475e-8c07-d62a30c7e683:123zO3xKCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP
- couchdb:
- host: couchdb
- volume-size: 10
- admin:
- user: whisk_admin
- password: some_passw0rd
- controller:
- user: invoker_admin
- password: s0meP@ass1
- invoker:
- user: controller_admin
- password: s0meP@ass2
- kafka:
- host: kafka
- volume-size: 10
- controller:
- protocol: "http"
- host : "controller"
- port: "3233"
- image: "ghcr.io/nuvolaris/openwhisk-controller"
- tag: "0.3.0-morpheus.22122609"
- invoker:
- protocol: "http"
- host : "invoker"
- port: "8080"
- image: "ghcr.io/nuvolaris/openwhisk-invoker"
- tag: "0.3.0-morpheus.22122609"
- scheduler:
- schedule: "* * * * *"
- tls:
- acme-registered-email: francesco@nuvolaris.io
- acme-server-url: https://acme-v02.api.letsencrypt.org/directory
- zookeeper:
- host: zookeeper
- data-volume-size: 10
- log-volume-size: 5
- configs:
- limits:
- actions:
- sequence-maxLength: 500
- invokes-perMinute: 9999
- invokes-concurrent: 2500
- triggers:
- fires-perMinute: 5000
- memory:
- limit-min: 128m
- limit-std: 256m
- limit-max: 2048m
- concurrency:
- limit-min: 1
- limit-std: 1
- limit-max: 1
- loadbalancer:
- timeout-factor: 2
- blackbox-fraction: 25%
- controller:
- javaOpts: "-Xmx2048M"
- loggingLevel: "INFO"
- invoker:
- javaOpts: "-Xmx8192M"
- loggingLevel: "INFO"
- containerPool:
- userMemory: "51200m"
- redis:
- volume-size: 5
- default:
- password: s0meP@ass3
- nuvolaris:
- prefix: nuv
- password: s0meP@ass3
- mongodb:
- host: mongodb
- volume-size: 10
- admin:
- user: whisk_admin
- password: 0therPa55
- nuvolaris:
- user: nuvolaris
- password: s0meP@ass3
- exposedExternally: False
- useOperator: False
- minio:
- volume-size: 10
- admin:
- user: minioadmin
- password: minioadmin
- nuvolaris:
- user: nuvolaris
- password: zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG
- monitoring:
- prometheus:
- volume-size: 10
- alert-manager:
- enabled: true
- volume-size: 2
- slack:
- enabled: true
- default: true
- slack_channel_name: '#monitoring-nuvolaris'
- slack_api_url: ${SLACK_API_URL}
- gmail:
- enabled: true
- default: false
- from: ${ALERT_EMAIL_FROM}
- to: ${ALERT_EMAIL_TO}
- username: ${GMAIL_USERNAME}
- password: ${GMAIL_PASSWORD}
- postgres:
- volume-size: 5
- replicas: 2
- admin:
- password: 0therPa55
- replica-password: 0therPa55RR
- nuvolaris:
- password: s0meP@ass3
\ No newline at end of file
diff --git a/tests/k3s/whisk.yaml b/tests/k3s/whisk.yaml
index 7894733..ba9f57b 100644
--- a/tests/k3s/whisk.yaml
+++ b/tests/k3s/whisk.yaml
@@ -48,7 +48,7 @@ spec:
# minio static enabled or not
static: true
# monitoring enabled or not
- monitoring: true
+ monitoring: false
# postgres enabled or not
postgres: true
openwhisk:
(openserverless-operator) 22/24: fix: cron action executor does not need to pass ow action parameters
Posted by fr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
franztt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openserverless-operator.git
commit bf78d7939f9e0c1a02e24287c005390e20841f0d
Author: francescotimperi <fr...@gmail.com>
AuthorDate: Sat Jul 27 15:45:01 2024 +0000
fix: cron action executor does not need to pass ow action parameters
---
nuvolaris/actionexecutor.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/nuvolaris/actionexecutor.py b/nuvolaris/actionexecutor.py
index 379a2fb..aa3fd3b 100644
--- a/nuvolaris/actionexecutor.py
+++ b/nuvolaris/actionexecutor.py
@@ -152,10 +152,10 @@ def call_ow_action(url, parameters, ow_auth):
try:
response = None
if(len(parameters)>0):
- response = req.post(url, auth=(ow_auth['username'],ow_auth['password']), headers=headers, data=json.dumps(parameters))
- else:
- #If the body is empty Content-Type must be not provided otherwise OpenWhisk api returns a 400 error
- response = req.post(url, auth=(ow_auth['username'],ow_auth['password']))
+ logging.info("calling an action with %s parameters",len(parameters))
+
+ # OpenWhisk passes automatically the registered parameters, so this cron executor does not need to pass them.
+ response = req.post(url, auth=(ow_auth['username'],ow_auth['password']))
if (response.status_code in [200,202]):
logging.info(f"call to {url} succeeded with {response.status_code}. Body {response.text}")
(openserverless-operator) 02/24: chore: code cleanup
Posted by fr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
franztt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openserverless-operator.git
commit 94b70e2461e42d3c859a4f8cd8cd94fdee221c89
Author: Francesco Timperi Tiberi <fr...@gmail.com>
AuthorDate: Tue Jul 9 17:39:31 2024 +0000
chore: code cleanup
chore: added github folder
chore: using mastrogpt tag
chore: fixing for image build
chore: fixing missing licenes
chore: fixing for license
chore: fir for license
chore: removing docker login
chore: added dcoker login
chore: fix for k3s local execution
---
.env.sample | 57 ++++
.github/cisetup.sh | 38 +++
.github/debug-github-actions.sh | 58 ++++
.github/kind.yaml | 74 +++++
.github/wait.sh | 27 ++
.github/workflows/check.yml | 80 +++++
.github/workflows/fast-image.yml | 84 +++++
.github/workflows/image.yml | 105 +++++++
.gitignore | 56 ++++
.licenserc.yaml | 62 ++++
Taskfile.yml | 8 +-
TaskfileEnterprise.yml | 71 -----
TaskfileNuvEnterprise.yml | 91 ------
actions/content/__main__.py | 17 +
actions/devel/download/__main__.py | 17 +
actions/devel/download/nuvolaris/config.py | 56 ++++
actions/devel/download/nuvolaris/couchdb_util.py | 6 +
actions/devel/ferretdb/nuvolaris/config.py | 56 ++++
actions/devel/ferretdb/nuvolaris/couchdb_util.py | 6 +
actions/devel/minio/nuvolaris/config.py | 56 ++++
actions/devel/minio/nuvolaris/couchdb_util.py | 6 +
actions/devel/psql/nuvolaris/config.py | 56 ++++
actions/devel/psql/nuvolaris/couchdb_util.py | 6 +
actions/devel/redis/nuvolaris/config.py | 56 ++++
actions/devel/redis/nuvolaris/couchdb_util.py | 6 +
actions/devel/upload/__main__.py | 17 +
actions/devel/upload/nuvolaris/config.py | 56 ++++
actions/devel/upload/nuvolaris/couchdb_util.py | 6 +
.../operator-clusterroles.yaml | 4 -
deploy/nuvolaris-permissions/operator-roles.yaml | 9 -
deploy/nuvolaris-permissions/whisk-crd.yaml | 45 +--
enterprise-util/faas/openwhisk-runtimes-cm.yaml | 289 -----------------
enterprise-util/faas/whisk_nuvorg.yaml | 152 ---------
enterprise/.git-keep | 0
.../faas/.git-keep => enterprise/.gitkeep | 0
nuvolaris/ceph_cos.py | 346 ---------------------
nuvolaris/ceph_cos_ingress.py | 150 ---------
nuvolaris/ceph_cos_obc_data.py | 134 --------
nuvolaris/ceph_cos_user_data.py | 137 --------
nuvolaris/kopf_util.py | 9 -
nuvolaris/main.py | 19 +-
nuvolaris/patcher.py | 8 -
nuvolaris/templates/redis_quota_checker.lua | 17 +
nuvolaris/templates/redis_quota_checker.sh | 16 +
nuvolaris/user_handlers.py | 10 +-
tests/k3s/whisk-demo-user.yaml | 47 ---
tests/k3s/whisk-full-arm.yaml | 172 ----------
tests/k3s/whisk-full-faas.yaml | 172 ----------
tests/k3s/whisk-full-mysc2.yaml | 181 -----------
tests/k3s/whisk-full-saas.yaml | 174 -----------
tests/k3s/whisk-full.yaml | 174 -----------
tests/k3s/whisk.yaml | 96 ++++--
tests/microk8s/monitoring-ingress.yaml | 17 +
53 files changed, 1194 insertions(+), 2418 deletions(-)
diff --git a/.env.sample b/.env.sample
new file mode 100644
index 0000000..fdbeda5
--- /dev/null
+++ b/.env.sample
@@ -0,0 +1,57 @@
+# 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.
+#
+# force detection when autodetect fails
+MY_DETECT=your-cluster
+## GitHub credentials
+# to access github AND also as a generic identity to tag vms
+GITHUB_USER=user
+GITHUB_TOKEN=token
+## AWS parameters
+# access
+AWS_ACCESS_KEY_ID=the-aws-id
+AWS_SECRET_ACCESS_KEY=the-aws-key
+AWS_DEFAULT_REGION=the-aws-region
+# used to build a vm in the choosen region
+AWS_AMI_ID_UBUNTU=ami-id-of-ubuntu-in-your-region
+# used only for opewhisk builds
+AWS_BUCKET=a-bucket-to-store-logs
+# real home outside your docker container
+REAL_HOME=your-actual-directory-outside-of-the-container
+# linode token
+LINODE_CLI_TOKEN=linode-token-from-dashboard
+# if you want to push your custom image to push in your repo
+MY_OPERATOR_IMAGE=user/nuvolaris-operator
+# a slack incoming webhook to write messages
+SLACK_WEBOOK=your-slack-webhook
+# ip or hostname and ssh user with sudo of a server running ubuntu 22
+# accessible with clusters/id_rsa.pub to install k3s
+K3S_HOST=your.k3s.host
+K3S_USER=ubuntu
+# openshift
+OPENSHIFT_APPS=apps.wfx.sciabarra.net
+OPENSHIFT_API=https://api.wfx.sciabarra.net:6443
+OPENSHIFT_USER=kubeadmin
+OPENSHIFT_REGISTRY=default-route-openshift-image-registry.apps.wfx.sciabarra.net
+OPENSHIFT_PASSWORD=your-openshift-password
+
+#enterprise monitoring
+SLACK_API_URL=your-slack-webhook-url
+GMAIL_USERNAME=your-gmail-username
+GMAIL_PASSWORD=your-gmail-app-password
+ALERT_EMAIL_FROM=your-gmail-from-account
+ALERT_EMAIL_TO=your-email-to-comma-separated-addresses
diff --git a/.github/cisetup.sh b/.github/cisetup.sh
new file mode 100644
index 0000000..8cee4fb
--- /dev/null
+++ b/.github/cisetup.sh
@@ -0,0 +1,38 @@
+#!/bin/bash
+# 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.
+#
+sudo sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin
+sudo apt-get -y install python3.11 python3.11-venv curl wget jq
+WSK_VERSION=1.2.0
+WSK_BASE=https://github.com/apache/openwhisk-cli/releases/download
+ARCH=amd64
+WSK_URL="$WSK_BASE/$WSK_VERSION/OpenWhisk_CLI-$WSK_VERSION-linux-$ARCH.tgz"
+curl -sSL https://install.python-poetry.org | python3.11 -
+curl -sSL "$WSK_URL" | tar xzvf - -C ~/.local/bin/
+VER="v4.5.4"
+ARCH="$(dpkg --print-architecture)"
+URL="https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2F$VER/kustomize_${VER}_linux_${ARCH}.tar.gz"
+curl -sL $URL | tar tzvf - -C ~/.local/bin
+YQ_VER=v4.27.2
+YQ_BIN=yq_linux_amd64
+sudo wget https://github.com/mikefarah/yq/releases/download/${YQ_VER}/${YQ_BIN} -O /usr/bin/yq && sudo chmod +x /usr/bin/yq
+MC_VER=RELEASE.2023-03-23T20-03-04Z
+sudo wget https://dl.min.io/client/mc/release/linux-${ARCH}/archive/mc.${MC_VER} -O /usr/bin/mc && sudo chmod +x /usr/bin/mc
+#URL="https://dl.k8s.io/release/$VER/bin/linux/$ARCH/kubectl"
+#curl -sSL "$URL" | sudo tee /usr/local/bin/kubectl && sudo chmod +x /usr/bin/kubectl
+#kubectl version
diff --git a/.github/debug-github-actions.sh b/.github/debug-github-actions.sh
new file mode 100755
index 0000000..d92184e
--- /dev/null
+++ b/.github/debug-github-actions.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+# 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.
+#
+
+
+if [[ -z "$NGROK_TOKEN" ]]; then
+ echo "Please set 'NGROK_TOKEN'"
+ exit 2
+fi
+
+if [[ -z "$USER_PASS" ]]; then
+ echo "Please set 'USER_PASS' for user: $USER"
+ exit 3
+fi
+
+echo "### Install ngrok ###"
+
+wget -q https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-386.zip
+unzip ngrok-stable-linux-386.zip
+chmod +x ./ngrok
+
+echo "### Update user: $USER password ###"
+echo -e "$USER_PASS\n$USER_PASS" | sudo passwd "$USER"
+
+echo "### Start ngrok proxy for 22 port ###"
+
+
+rm -f .ngrok.log
+./ngrok authtoken "$NGROK_TOKEN"
+./ngrok tcp 22 --log ".ngrok.log" &
+
+sleep 10
+HAS_ERRORS=$(grep "command failed" < .ngrok.log)
+
+if [[ -z "$HAS_ERRORS" ]]; then
+ echo ""
+ echo "=========================================="
+ echo "To connect: $(grep -o -E "tcp://(.+)" < .ngrok.log | sed "s/tcp:\/\//ssh $USER@/" | sed "s/:/ -p /")"
+ echo "=========================================="
+else
+ echo "$HAS_ERRORS"
+ exit 4
+fi
diff --git a/.github/kind.yaml b/.github/kind.yaml
new file mode 100644
index 0000000..45d901e
--- /dev/null
+++ b/.github/kind.yaml
@@ -0,0 +1,74 @@
+# 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.
+#
+kind: Cluster
+apiVersion: kind.x-k8s.io/v1alpha4
+nodes:
+- role: control-plane
+ kubeadmConfigPatches:
+ - |
+ kind: InitConfiguration
+ nodeRegistration:
+ kubeletExtraArgs:
+ node-labels: "ingress-ready=true,nuvolaris.io/apihost=localhost,nuvolaris.io/apiport=3233,nuvolaris.io/protocol=http,nuvolaris.io/kube=kind,nuvolaris.io/hostpath=data"
+- role: worker
+ extraMounts:
+ - hostPath: /tmp
+ containerPath: /data
+ extraPortMappings:
+ - containerPort: 80
+ hostPort: 80
+ protocol: TCP
+ - containerPort: 443
+ hostPort: 443
+ protocol: TCP
+ - containerPort: 30232
+ hostPort: 3232
+ protocol: TCP
+ - containerPort: 30233
+ hostPort: 3233
+ protocol: TCP
+ - containerPort: 30896
+ hostPort: 7896
+ protocol: TCP
+ - containerPort: 30984
+ hostPort: 5984
+ protocol: TCP
+ - containerPort: 30992
+ hostPort: 9092
+ protocol: TCP
+ - containerPort: 30644
+ hostPort: 9644
+ protocol: TCP
+ - containerPort: 6379
+ hostPort: 30379
+ protocol: TCP
+ - containerPort: 27017
+ hostPort: 32717
+ protocol: TCP
+ - containerPort: 28017
+ hostPort: 32817
+ protocol: TCP
+ - containerPort: 32717
+ hostPort: 27017
+ protocol: TCP
+ - containerPort: 32090
+ hostPort: 9000
+ protocol: TCP
+ - containerPort: 32091
+ hostPort: 9090
+ protocol: TCP
diff --git a/.github/wait.sh b/.github/wait.sh
new file mode 100755
index 0000000..5f9e39a
--- /dev/null
+++ b/.github/wait.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+# 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.
+#
+echo "You have an hour to debug this build"
+echo 'do "touch /tmp/continue" to stop the wait'
+for i in $(seq 1 60)
+do echo $i
+ sleep 60
+ if test -e /tmp/continue
+ then break
+ fi
+done
diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml
new file mode 100644
index 0000000..fb030d8
--- /dev/null
+++ b/.github/workflows/check.yml
@@ -0,0 +1,80 @@
+# 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.
+#
+#
+# Licensed 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.
+
+name: openserverless-operator
+on:
+ push:
+ tags-ignore:
+ - '*'
+
+jobs:
+ check:
+ name: Build
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+ with:
+ submodules: recursive
+ - name: License
+ uses: apache/skywalking-eyes@main
+ continue-on-error: true
+ - name: Make envfile
+ if: ${{ env.GITHUB_USER != 'nuvolaris'}}
+ uses: SpicyPizza/create-envfile@v1.3
+ with:
+ envkey_MY_OPERATOR_IMAGE: ${{ secrets.MY_OPERATOR_IMAGE }}
+ file_name: .env
+ fail_on_empty: false
+ - name: Kind
+ uses: helm/kind-action@v1.2.0
+ with:
+ cluster_name: nuvolaris
+ config: .github/kind.yaml
+ - name: Setup
+ run: bash .github/cisetup.sh
+ - name: Install
+ run: task setup
+ - name: BuildAndLoad
+ run: task build-and-load
+ - name: AddIngress
+ run: task kind:ingress
+ - name: Test
+ run: task test
+ continue-on-error: true
+ - name: Ngrok
+ if: ${{ env.NGROK == 'on' }}
+ run: bash .github/debug-github-actions.sh
+ env:
+ NGROK_TOKEN: ${{ secrets.NGROK_TOKEN }}
+ USER_PASS: ${{ secrets.USER_PASS }}
+ - name: Wait
+ if: ${{ env.NGROK == 'on' }}
+ run: bash .github/wait.sh
diff --git a/.github/workflows/fast-image.yml b/.github/workflows/fast-image.yml
new file mode 100644
index 0000000..65eb5ce
--- /dev/null
+++ b/.github/workflows/fast-image.yml
@@ -0,0 +1,84 @@
+# 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.
+#
+# Copyright 2021 Nuvolaris LLC
+#
+# Licensed 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.
+
+name: Build Fast Image
+
+on:
+ push:
+ tags:
+ - 'fast[0-9]*'
+ branches-ignore:
+ - '*'
+
+jobs:
+ image:
+ name: Image
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+ with:
+ submodules: recursive
+ - name: Make envfile
+ if: ${{ github.repository_owner != 'nuvolaris'}}
+ uses: SpicyPizza/create-envfile@v1.3
+ with:
+ envkey_MY_OPERATOR_IMAGE: ${{ secrets.MY_OPERATOR_IMAGE }}
+ envkey_GITHUB_TOKEN: ${{ secrets.GT_TOKEN }}
+ envkey_GITHUB_USER: ${{ secrets.GT_USER }}
+ file_name: .env
+ fail_on_empty: false
+ - name: Registry
+ uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
+ with:
+ registry: ghcr.io
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+ - name: Setup
+ run: bash .github/cisetup.sh
+ - name: Ngrok
+ if: ${{ env.NGROK == 'on' }}
+ run: bash .github/debug-github-actions.sh
+ env:
+ NGROK_TOKEN: ${{ secrets.NGROK_TOKEN }}
+ USER_PASS: ${{ secrets.USER_PASS }}
+ - name: Wait
+ if: ${{ env.NGROK == 'on' }}
+ run: bash .github/wait.sh
+ - name: DockerLogin
+ if: ${{ github.repository_owner != 'nuvolaris'}}
+ run: task docker-login
+ - name: Buildx
+ uses: crazy-max/ghaction-docker-buildx@v1
+ with:
+ version: latest
+ - name: Image
+ run: task buildx-and-push
\ No newline at end of file
diff --git a/.github/workflows/image.yml b/.github/workflows/image.yml
new file mode 100644
index 0000000..2e44bd4
--- /dev/null
+++ b/.github/workflows/image.yml
@@ -0,0 +1,105 @@
+# 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.
+#
+# Copyright 2021 Nuvolaris LLC
+#
+# Licensed 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.
+
+name: Build Image
+
+on:
+ push:
+ tags:
+ - '[0-9]*'
+ branches-ignore:
+ - '*'
+
+jobs:
+ image:
+ name: Image
+ runs-on: ubuntu-latest
+ steps:
+ - name: Remove unnecessary files
+ run: |
+ sudo rm -rf /usr/share/dotnet
+ sudo rm -rf "$AGENT_TOOLSDIRECTORY"
+ - name: Checkout
+ uses: actions/checkout@v2
+ with:
+ submodules: recursive
+ - name: License
+ uses: apache/skywalking-eyes@main
+ - name: Make envfile
+ if: ${{ github.repository_owner != 'nuvolaris'}}
+ uses: SpicyPizza/create-envfile@v1.3
+ with:
+ envkey_MY_OPERATOR_IMAGE: ${{ secrets.MY_OPERATOR_IMAGE }}
+ envkey_GITHUB_TOKEN: ${{ secrets.GT_TOKEN }}
+ envkey_GITHUB_USER: ${{ secrets.GT_USER }}
+ file_name: .env
+ fail_on_empty: false
+ - name: Registry
+ uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
+ with:
+ registry: ghcr.io
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+ - name: Setup
+ run: bash .github/cisetup.sh
+ - name: Kind
+ uses: helm/kind-action@v1.2.0
+ with:
+ cluster_name: nuvolaris
+ config: .github/kind.yaml
+ - name: Install
+ run: poetry install
+ #- name: Tag
+ # run: task image-tag
+ #- name: BuildAndLoad
+ # run: task build-and-load
+ #- name: Ngrok
+ # if: ${{ env.NGROK == 'on' }}
+ # run: bash .github/debug-github-actions.sh
+ # env:
+ # NGROK_TOKEN: ${{ secrets.NGROK_TOKEN }}
+ # USER_PASS: ${{ secrets.USER_PASS }}
+ #- name: Wait
+ # if: ${{ env.NGROK == 'on' }}
+ # run: bash .github/wait.sh
+ #- name: AddIngress
+ # run: task kind:ingress
+ #- name: Test
+ # run: task test
+ - name: DockerLogin
+ if: ${{ github.repository_owner != 'nuvolaris'}}
+ run: task docker-login
+ - name: Buildx
+ uses: crazy-max/ghaction-docker-buildx@v1
+ with:
+ version: latest
+ - name: Image
+ run: task buildx-and-push
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5522c66
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,56 @@
+# 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.
+#
+.git-hooks/
+__pycache__/
+__pypackages__
+.pdm.toml
+.*-version
+kubeconfig
+openwhisk-deploy-kube/
+kustomization.yaml
+playground
+examples/
+aliases
+deploy/extract/
+deploy/whisk-system/
+deploy/**/*_generated.yaml
+actions/**/*.zip
+node_modules/
+.env
+__*
+clusters/id_rsa*
+clusters/controlplane.yaml
+clusters/talosconfig
+clusters/worker.yaml
+*.kubeconfig
+run.pid
+*.off
+actions/**/nuvolaris/**/*
+!actions/**/__main__.py
+_*.yaml
+apihost.txt
+.git-hooks/
+installer
+whisk.properties
+.task/
+*.log
+.latestcheck
+_failed.txt
+deploy.tar
+actions/github/callback/common
+actions/github/webhook/common
\ No newline at end of file
diff --git a/.licenserc.yaml b/.licenserc.yaml
new file mode 100644
index 0000000..ae878bd
--- /dev/null
+++ b/.licenserc.yaml
@@ -0,0 +1,62 @@
+# 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.
+#
+header:
+ license:
+ spdx-id: Apache-2.0
+ copyright-owner: Apache Software Foundation
+
+ paths-ignore:
+ - '**/.git-hooks/*'
+ - 'playground'
+ - '**/*.off'
+ - '**/*.excalidraw'
+ - '.pdm.toml'
+ - 'nodesource_setup.sh'
+ - '**/pdm.lock'
+ - '**/LICENSE'
+ - '**/package-lock.json'
+ - '**/.*-version'
+ - '**/*.json'
+ - '**/*.txt'
+ - '**/node_modules/**'
+ - '**/__pypackages__/**'
+ - '**/build/**'
+ - '**/target/**'
+ - '**/openwhisk/**'
+ - '**/openwhisk-*/**'
+ - '**/parthenon/**'
+ - '**/*.kubeconfig'
+ - '**/license-eye'
+ - '**/id_rsa*'
+ - '**/.gitkeep'
+ - 'poetry.lock'
+ - '**/__*'
+ - '.github/ngrok'
+ - 'ngrok'
+ - 'ngrok.log'
+ - 'poetry.lock'
+ - 'installer/**'
+ - 'nuv/**'
+ - 'olaris/**'
+ - 'skip-test'
+
+
+dependency:
+ files:
+ - go.mod
+ - package.json
diff --git a/Taskfile.yml b/Taskfile.yml
index e6b17ef..a88e0cd 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -57,9 +57,7 @@ includes:
dir: clusters
mik:
taskfile: clusters/microk8s.yml
- dir: clusters
- ent:
- taskfile: TaskfileEnterprise.yml
+ dir: clusters
k3s:
taskfile: clusters/k3s.yml
dir: clusters
@@ -71,9 +69,7 @@ includes:
dir: actions
talos:
taskfile: clusters/talos-gcp.yml
- dir: clusters
- nuvent:
- taskfile: TaskfileNuvEnterprise.yml
+ dir: clusters
tasks:
diff --git a/TaskfileEnterprise.yml b/TaskfileEnterprise.yml
deleted file mode 100644
index 2a1ae28..0000000
--- a/TaskfileEnterprise.yml
+++ /dev/null
@@ -1,71 +0,0 @@
-# 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.
-#
-version: '3'
-
-vars:
- # taken from the tag
- OPERATOR_TAG:
- sh: git describe --tags --abbrev=0 2>/dev/null || git rev-parse --short HEAD
- # taken from the Dockerfile - ovverdable with MY_OPERATOR_IMAGE
- OPERATOR_IMAGE:
- sh: awk -F= '/ARG OPERATOR_IMAGE_DEFAULT=/ { print $2 ; exit }' Dockerfile
-
-tasks:
- image:
- cmds:
- - echo "Operator=${MY_OPERATOR_IMAGE:-{{.OPERATOR_IMAGE}}}:{{.OPERATOR_TAG}}"
- silent: true
-
- docker-login: >
- echo $DOCKER_HUB_TOKEN | docker login registry.hub.docker.com -u $DOCKER_HUB_USER --password-stdin
-
- build: >
- docker build
- -t ${MY_OPERATOR_IMAGE:-{{.OPERATOR_IMAGE}}}:{{.OPERATOR_TAG}}
- --build-arg=OPERATOR_IMAGE_DEFAULT=${MY_OPERATOR_IMAGE:-{{.OPERATOR_IMAGE}}}
- --build-arg=OPERATOR_TAG_DEFAULT={{.OPERATOR_TAG}}
- {{.CLI_ARGS}} .
-
- build-and-push:
- - task: docker-login
- - task: build
- - docker push ${MY_OPERATOR_IMAGE:-{{.OPERATOR_IMAGE}}}:{{.OPERATOR_TAG}}
-
- build-and-load:
- - task: build
- - >
- kind load docker-image
- ${MY_OPERATOR_IMAGE:-{{.OPERATOR_IMAGE}}}:{{.OPERATOR_TAG}}
- --name=nuvolaris
-
- docker-setup:
- - docker buildx create --use
- - docker run -it --rm --privileged tonistiigi/binfmt --install all
-
- buildx-and-push:
- - task: docker-setup
- - >
- docker buildx build
- --platform linux/amd64,linux/arm64
- -t ${MY_OPERATOR_IMAGE:-{{.OPERATOR_IMAGE}}}:{{.OPERATOR_TAG}}
- --build-arg OPERATOR_IMAGE_DEFAULT=${MY_OPERATOR_IMAGE:-{{.OPERATOR_IMAGE}}}
- --build-arg OPERATOR_TAG_DEFAULT={{.OPERATOR_TAG}}
- . --push
-
- docker-hub-authorize: >
- kubectl create secret docker-registry regcred --docker-server=registry.hub.docker.com --docker-username=$DOCKER_HUB_USER --docker-password=$DOCKER_HUB_TOKEN
diff --git a/TaskfileNuvEnterprise.yml b/TaskfileNuvEnterprise.yml
deleted file mode 100644
index 56256da..0000000
--- a/TaskfileNuvEnterprise.yml
+++ /dev/null
@@ -1,91 +0,0 @@
-# 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.
-#
-version: '3'
-
-tasks:
- devcluster:delete:
- - nuv ent setup devcluster --uninstall
-
- devcluster:
- - nuv ent config enable --prometheus --slack --quota
- - nuv ent config slack --apiurl=$SLACK_API_URL --channel=#nuvolaris-monitoring
- - nuv ent config license nuvolaris --registry=registry.hub.docker.com --username=$DOCKER_HUB_USER --token=$DOCKER_HUB_TOKEN
- - nuv ent config controller --javaopts=2 --replicas=1 --loglevel=INFO
- - nuv ent config invoker --javaopts=2 --poolmemory=4 --timeoutsrun=5 --timeoutslogs=1 --replicas=1 --loglevel=INFO
- - nuv ent config postgres --backup --schedule='30 * * * *' --replicas=2
- - nuv ent config minio --s3 --console
- - nuv ent setup devcluster
-
- devcluster:full:
- - nuv config reset
- - nuv config enable --all
- - task: devcluster
-
- devcluster:minimal:
- - nuv config reset
- - nuv config disable --all
- - task: devcluster
-
- gc-deploy:
- - nuv ent config controller --javaopts=-Xmx2048M
- - nuv ent config invoker --javaopts=-Xmx2048M --poolmemory=8192m
- - nuv ent config volumes --couchdb=30 --kafka=30 --database=50 --storage=50 --alerting=20
- - nuv ent config storage --class=rook-ceph-block --provisioner=rook-ceph.rbd.csi.ceph.com
- - nuv ent config enable --prometheus --slack
- - nuv ent config slack --apiurl=$SLACK_API_URL --channel=#nuvolaris-monitoring
- - nuv ent config license nuvolaris --registry=registry.hub.docker.com --username=$DOCKER_HUB_USER --token=$DOCKER_HUB_TOKEN
- - nuv ent setup cluster
-
- saas1:minimal:
- - nuv config reset
- - nuv config disable --all
- - nuv config apihost 146.148.119.191
- - task: gc-deploy
-
- saas1:full:
- - nuv config reset
- - nuv config enable --all
- - nuv config apihost 146.148.119.191
- - task: gc-deploy
-
- nuvolaris-app:minimal:
- - nuv config reset
- - nuv config disable --all
- - nuv config apihost 35.242.186.170
- - task: gc-deploy
-
- nuvolaris-app:full:
- - nuv config reset
- - nuv config enable --all
- - nuv config apihost 35.242.186.170
- - task: gc-deploy
-
- gc-cluster-kube:
- - nuv ent saas admin gc-kubeconfig {{.N}}
-
- gc-prepare:
- - nuv ent saas gcloud vpc-create app-vpc
- - nuv ent saas config gcloud --ent_name=app --ent_domain=nuvolaris.app --gc_region=europe-west2 --gc_zone=europe-west2-a
-
- gc-cluster-delete:
- - nuv ent saas admin gc-cluster-delete {{.N}}
-
- gc-cluster-create:
- - nuv ent saas admin gc-cluster-create {{.N}} --token=$GITHUB_OLARIS_CLONE_TOKEN
-
-
diff --git a/actions/content/__main__.py b/actions/content/__main__.py
index 908868a..305864d 100644
--- a/actions/content/__main__.py
+++ b/actions/content/__main__.py
@@ -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 os
import base64
import random
diff --git a/actions/devel/download/__main__.py b/actions/devel/download/__main__.py
index 080f45b..c56ba11 100644
--- a/actions/devel/download/__main__.py
+++ b/actions/devel/download/__main__.py
@@ -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 os
import base64
import random
diff --git a/actions/devel/download/nuvolaris/config.py b/actions/devel/download/nuvolaris/config.py
index 787d4a7..283ab9f 100644
--- a/actions/devel/download/nuvolaris/config.py
+++ b/actions/devel/download/nuvolaris/config.py
@@ -174,6 +174,61 @@ def detect_storage(storages=None):
pass
return res
+def detect_object_storage(object_storages=None):
+ """
+ Detect the basic Object Store setup if enabled
+ """
+ res = {}
+ if not object_storages:
+ import nuvolaris.util as util
+
+ try:
+ detect_object_store = True
+ detect_object_store_rgw_srv_name = True
+ detect_object_store_rgw_srv_port = True
+
+ # skips autodetection if cephobjectstore is not active
+ if not exists('components.cosi') or not get('components.cosi'):
+ logging.info(f"*** cephobjectstore support is not requested skipping auto configuration check")
+ return res
+
+ # skips autodetection if already provided
+ if exists('cosi.bucket_storageclass') and get('cosi.bucket_storageclass') != 'auto':
+ logging.info(f"*** configuration provided already a cosi.bucket_storageclass={get('cosi.bucket_storageclass')}")
+ detect_object_store = False
+
+ if exists('cosi.rgwservice_name') and get('cosi.rgwservice_name') != 'auto':
+ logging.info(f"*** configuration provided already a cosi.rgwservice_name={get('cosi.rgwservice_name')}")
+ detect_object_store_rgw_srv_name = False
+
+ if exists('cosi.rgwservice_port') and get('cosi.rgwservice_port') != 'auto':
+ logging.info(f"*** configuration provided already a cosi.rgwservice_port={get('cosi.rgwservice_port')}")
+ detect_object_store_rgw_srv_port = False
+
+ if not detect_object_store and not detect_object_store_rgw_srv_name and not detect_object_store_rgw_srv_port:
+ return res
+
+ if detect_object_store:
+ storage_class = util.get_object_storage_class()
+ if storage_class:
+ res['cosi.bucket_storageclass'] = storage_class
+ _config['cosi.bucket_storageclass'] = storage_class
+
+ if detect_object_store_rgw_srv_name:
+ rgwsrv = util.get_object_storage_rgw_srv_name()
+ if(rgwsrv):
+ res['cosi.rgwservice_name'] = rgwsrv
+ _config['cosi.rgwservice_name'] = rgwsrv
+
+ if detect_object_store_rgw_srv_port:
+ rgwport = util.get_object_storage_rgw_srv_http_port()
+ if(rgwport):
+ res['cosi.rgwservice_port'] = rgwport
+ _config['cosi.rgwservice_port'] = rgwport
+ except:
+ pass
+ return res
+
def detect_env():
_config['operator.image'] = os.environ.get("OPERATOR_IMAGE", "missing-OPERATOR_IMAGE")
_config['operator.tag'] = os.environ.get("OPERATOR_TAG", "missing-OPERATOR_TAG")
@@ -195,6 +250,7 @@ def detect():
detect_storage()
detect_labels()
detect_env()
+ detect_object_storage()
def dump_config():
import nuvolaris.config as cfg
diff --git a/actions/devel/download/nuvolaris/couchdb_util.py b/actions/devel/download/nuvolaris/couchdb_util.py
index 3adb6b9..7a18899 100644
--- a/actions/devel/download/nuvolaris/couchdb_util.py
+++ b/actions/devel/download/nuvolaris/couchdb_util.py
@@ -127,6 +127,12 @@ class CouchDB:
r = self.db_session.put(url, data=data)
return r.status_code == 200
+ def enable_db_compaction(self,db_name):
+ url = f"{self.db_url}/_node/_local/_config/compactions/{self.db_prefix}{db_name}"
+ data = '[{db_fragmentation, \"60%\"}, {view_fragmentation, \"60%\"}]'
+ r = self.db_session.put(url, json=data)
+ return r.status_code == 200
+
def add_user(self, username: str, password: str):
userpass = {"name": username, "password": password, "roles": [], "type": "user"}
url = f"{self.db_url}/_users/org.couchdb.user:{username}"
diff --git a/actions/devel/ferretdb/nuvolaris/config.py b/actions/devel/ferretdb/nuvolaris/config.py
index 787d4a7..283ab9f 100644
--- a/actions/devel/ferretdb/nuvolaris/config.py
+++ b/actions/devel/ferretdb/nuvolaris/config.py
@@ -174,6 +174,61 @@ def detect_storage(storages=None):
pass
return res
+def detect_object_storage(object_storages=None):
+ """
+ Detect the basic Object Store setup if enabled
+ """
+ res = {}
+ if not object_storages:
+ import nuvolaris.util as util
+
+ try:
+ detect_object_store = True
+ detect_object_store_rgw_srv_name = True
+ detect_object_store_rgw_srv_port = True
+
+ # skips autodetection if cephobjectstore is not active
+ if not exists('components.cosi') or not get('components.cosi'):
+ logging.info(f"*** cephobjectstore support is not requested skipping auto configuration check")
+ return res
+
+ # skips autodetection if already provided
+ if exists('cosi.bucket_storageclass') and get('cosi.bucket_storageclass') != 'auto':
+ logging.info(f"*** configuration provided already a cosi.bucket_storageclass={get('cosi.bucket_storageclass')}")
+ detect_object_store = False
+
+ if exists('cosi.rgwservice_name') and get('cosi.rgwservice_name') != 'auto':
+ logging.info(f"*** configuration provided already a cosi.rgwservice_name={get('cosi.rgwservice_name')}")
+ detect_object_store_rgw_srv_name = False
+
+ if exists('cosi.rgwservice_port') and get('cosi.rgwservice_port') != 'auto':
+ logging.info(f"*** configuration provided already a cosi.rgwservice_port={get('cosi.rgwservice_port')}")
+ detect_object_store_rgw_srv_port = False
+
+ if not detect_object_store and not detect_object_store_rgw_srv_name and not detect_object_store_rgw_srv_port:
+ return res
+
+ if detect_object_store:
+ storage_class = util.get_object_storage_class()
+ if storage_class:
+ res['cosi.bucket_storageclass'] = storage_class
+ _config['cosi.bucket_storageclass'] = storage_class
+
+ if detect_object_store_rgw_srv_name:
+ rgwsrv = util.get_object_storage_rgw_srv_name()
+ if(rgwsrv):
+ res['cosi.rgwservice_name'] = rgwsrv
+ _config['cosi.rgwservice_name'] = rgwsrv
+
+ if detect_object_store_rgw_srv_port:
+ rgwport = util.get_object_storage_rgw_srv_http_port()
+ if(rgwport):
+ res['cosi.rgwservice_port'] = rgwport
+ _config['cosi.rgwservice_port'] = rgwport
+ except:
+ pass
+ return res
+
def detect_env():
_config['operator.image'] = os.environ.get("OPERATOR_IMAGE", "missing-OPERATOR_IMAGE")
_config['operator.tag'] = os.environ.get("OPERATOR_TAG", "missing-OPERATOR_TAG")
@@ -195,6 +250,7 @@ def detect():
detect_storage()
detect_labels()
detect_env()
+ detect_object_storage()
def dump_config():
import nuvolaris.config as cfg
diff --git a/actions/devel/ferretdb/nuvolaris/couchdb_util.py b/actions/devel/ferretdb/nuvolaris/couchdb_util.py
index 3adb6b9..7a18899 100644
--- a/actions/devel/ferretdb/nuvolaris/couchdb_util.py
+++ b/actions/devel/ferretdb/nuvolaris/couchdb_util.py
@@ -127,6 +127,12 @@ class CouchDB:
r = self.db_session.put(url, data=data)
return r.status_code == 200
+ def enable_db_compaction(self,db_name):
+ url = f"{self.db_url}/_node/_local/_config/compactions/{self.db_prefix}{db_name}"
+ data = '[{db_fragmentation, \"60%\"}, {view_fragmentation, \"60%\"}]'
+ r = self.db_session.put(url, json=data)
+ return r.status_code == 200
+
def add_user(self, username: str, password: str):
userpass = {"name": username, "password": password, "roles": [], "type": "user"}
url = f"{self.db_url}/_users/org.couchdb.user:{username}"
diff --git a/actions/devel/minio/nuvolaris/config.py b/actions/devel/minio/nuvolaris/config.py
index 787d4a7..283ab9f 100644
--- a/actions/devel/minio/nuvolaris/config.py
+++ b/actions/devel/minio/nuvolaris/config.py
@@ -174,6 +174,61 @@ def detect_storage(storages=None):
pass
return res
+def detect_object_storage(object_storages=None):
+ """
+ Detect the basic Object Store setup if enabled
+ """
+ res = {}
+ if not object_storages:
+ import nuvolaris.util as util
+
+ try:
+ detect_object_store = True
+ detect_object_store_rgw_srv_name = True
+ detect_object_store_rgw_srv_port = True
+
+ # skips autodetection if cephobjectstore is not active
+ if not exists('components.cosi') or not get('components.cosi'):
+ logging.info(f"*** cephobjectstore support is not requested skipping auto configuration check")
+ return res
+
+ # skips autodetection if already provided
+ if exists('cosi.bucket_storageclass') and get('cosi.bucket_storageclass') != 'auto':
+ logging.info(f"*** configuration provided already a cosi.bucket_storageclass={get('cosi.bucket_storageclass')}")
+ detect_object_store = False
+
+ if exists('cosi.rgwservice_name') and get('cosi.rgwservice_name') != 'auto':
+ logging.info(f"*** configuration provided already a cosi.rgwservice_name={get('cosi.rgwservice_name')}")
+ detect_object_store_rgw_srv_name = False
+
+ if exists('cosi.rgwservice_port') and get('cosi.rgwservice_port') != 'auto':
+ logging.info(f"*** configuration provided already a cosi.rgwservice_port={get('cosi.rgwservice_port')}")
+ detect_object_store_rgw_srv_port = False
+
+ if not detect_object_store and not detect_object_store_rgw_srv_name and not detect_object_store_rgw_srv_port:
+ return res
+
+ if detect_object_store:
+ storage_class = util.get_object_storage_class()
+ if storage_class:
+ res['cosi.bucket_storageclass'] = storage_class
+ _config['cosi.bucket_storageclass'] = storage_class
+
+ if detect_object_store_rgw_srv_name:
+ rgwsrv = util.get_object_storage_rgw_srv_name()
+ if(rgwsrv):
+ res['cosi.rgwservice_name'] = rgwsrv
+ _config['cosi.rgwservice_name'] = rgwsrv
+
+ if detect_object_store_rgw_srv_port:
+ rgwport = util.get_object_storage_rgw_srv_http_port()
+ if(rgwport):
+ res['cosi.rgwservice_port'] = rgwport
+ _config['cosi.rgwservice_port'] = rgwport
+ except:
+ pass
+ return res
+
def detect_env():
_config['operator.image'] = os.environ.get("OPERATOR_IMAGE", "missing-OPERATOR_IMAGE")
_config['operator.tag'] = os.environ.get("OPERATOR_TAG", "missing-OPERATOR_TAG")
@@ -195,6 +250,7 @@ def detect():
detect_storage()
detect_labels()
detect_env()
+ detect_object_storage()
def dump_config():
import nuvolaris.config as cfg
diff --git a/actions/devel/minio/nuvolaris/couchdb_util.py b/actions/devel/minio/nuvolaris/couchdb_util.py
index 3adb6b9..7a18899 100644
--- a/actions/devel/minio/nuvolaris/couchdb_util.py
+++ b/actions/devel/minio/nuvolaris/couchdb_util.py
@@ -127,6 +127,12 @@ class CouchDB:
r = self.db_session.put(url, data=data)
return r.status_code == 200
+ def enable_db_compaction(self,db_name):
+ url = f"{self.db_url}/_node/_local/_config/compactions/{self.db_prefix}{db_name}"
+ data = '[{db_fragmentation, \"60%\"}, {view_fragmentation, \"60%\"}]'
+ r = self.db_session.put(url, json=data)
+ return r.status_code == 200
+
def add_user(self, username: str, password: str):
userpass = {"name": username, "password": password, "roles": [], "type": "user"}
url = f"{self.db_url}/_users/org.couchdb.user:{username}"
diff --git a/actions/devel/psql/nuvolaris/config.py b/actions/devel/psql/nuvolaris/config.py
index 787d4a7..283ab9f 100644
--- a/actions/devel/psql/nuvolaris/config.py
+++ b/actions/devel/psql/nuvolaris/config.py
@@ -174,6 +174,61 @@ def detect_storage(storages=None):
pass
return res
+def detect_object_storage(object_storages=None):
+ """
+ Detect the basic Object Store setup if enabled
+ """
+ res = {}
+ if not object_storages:
+ import nuvolaris.util as util
+
+ try:
+ detect_object_store = True
+ detect_object_store_rgw_srv_name = True
+ detect_object_store_rgw_srv_port = True
+
+ # skips autodetection if cephobjectstore is not active
+ if not exists('components.cosi') or not get('components.cosi'):
+ logging.info(f"*** cephobjectstore support is not requested skipping auto configuration check")
+ return res
+
+ # skips autodetection if already provided
+ if exists('cosi.bucket_storageclass') and get('cosi.bucket_storageclass') != 'auto':
+ logging.info(f"*** configuration provided already a cosi.bucket_storageclass={get('cosi.bucket_storageclass')}")
+ detect_object_store = False
+
+ if exists('cosi.rgwservice_name') and get('cosi.rgwservice_name') != 'auto':
+ logging.info(f"*** configuration provided already a cosi.rgwservice_name={get('cosi.rgwservice_name')}")
+ detect_object_store_rgw_srv_name = False
+
+ if exists('cosi.rgwservice_port') and get('cosi.rgwservice_port') != 'auto':
+ logging.info(f"*** configuration provided already a cosi.rgwservice_port={get('cosi.rgwservice_port')}")
+ detect_object_store_rgw_srv_port = False
+
+ if not detect_object_store and not detect_object_store_rgw_srv_name and not detect_object_store_rgw_srv_port:
+ return res
+
+ if detect_object_store:
+ storage_class = util.get_object_storage_class()
+ if storage_class:
+ res['cosi.bucket_storageclass'] = storage_class
+ _config['cosi.bucket_storageclass'] = storage_class
+
+ if detect_object_store_rgw_srv_name:
+ rgwsrv = util.get_object_storage_rgw_srv_name()
+ if(rgwsrv):
+ res['cosi.rgwservice_name'] = rgwsrv
+ _config['cosi.rgwservice_name'] = rgwsrv
+
+ if detect_object_store_rgw_srv_port:
+ rgwport = util.get_object_storage_rgw_srv_http_port()
+ if(rgwport):
+ res['cosi.rgwservice_port'] = rgwport
+ _config['cosi.rgwservice_port'] = rgwport
+ except:
+ pass
+ return res
+
def detect_env():
_config['operator.image'] = os.environ.get("OPERATOR_IMAGE", "missing-OPERATOR_IMAGE")
_config['operator.tag'] = os.environ.get("OPERATOR_TAG", "missing-OPERATOR_TAG")
@@ -195,6 +250,7 @@ def detect():
detect_storage()
detect_labels()
detect_env()
+ detect_object_storage()
def dump_config():
import nuvolaris.config as cfg
diff --git a/actions/devel/psql/nuvolaris/couchdb_util.py b/actions/devel/psql/nuvolaris/couchdb_util.py
index 3adb6b9..7a18899 100644
--- a/actions/devel/psql/nuvolaris/couchdb_util.py
+++ b/actions/devel/psql/nuvolaris/couchdb_util.py
@@ -127,6 +127,12 @@ class CouchDB:
r = self.db_session.put(url, data=data)
return r.status_code == 200
+ def enable_db_compaction(self,db_name):
+ url = f"{self.db_url}/_node/_local/_config/compactions/{self.db_prefix}{db_name}"
+ data = '[{db_fragmentation, \"60%\"}, {view_fragmentation, \"60%\"}]'
+ r = self.db_session.put(url, json=data)
+ return r.status_code == 200
+
def add_user(self, username: str, password: str):
userpass = {"name": username, "password": password, "roles": [], "type": "user"}
url = f"{self.db_url}/_users/org.couchdb.user:{username}"
diff --git a/actions/devel/redis/nuvolaris/config.py b/actions/devel/redis/nuvolaris/config.py
index 787d4a7..283ab9f 100644
--- a/actions/devel/redis/nuvolaris/config.py
+++ b/actions/devel/redis/nuvolaris/config.py
@@ -174,6 +174,61 @@ def detect_storage(storages=None):
pass
return res
+def detect_object_storage(object_storages=None):
+ """
+ Detect the basic Object Store setup if enabled
+ """
+ res = {}
+ if not object_storages:
+ import nuvolaris.util as util
+
+ try:
+ detect_object_store = True
+ detect_object_store_rgw_srv_name = True
+ detect_object_store_rgw_srv_port = True
+
+ # skips autodetection if cephobjectstore is not active
+ if not exists('components.cosi') or not get('components.cosi'):
+ logging.info(f"*** cephobjectstore support is not requested skipping auto configuration check")
+ return res
+
+ # skips autodetection if already provided
+ if exists('cosi.bucket_storageclass') and get('cosi.bucket_storageclass') != 'auto':
+ logging.info(f"*** configuration provided already a cosi.bucket_storageclass={get('cosi.bucket_storageclass')}")
+ detect_object_store = False
+
+ if exists('cosi.rgwservice_name') and get('cosi.rgwservice_name') != 'auto':
+ logging.info(f"*** configuration provided already a cosi.rgwservice_name={get('cosi.rgwservice_name')}")
+ detect_object_store_rgw_srv_name = False
+
+ if exists('cosi.rgwservice_port') and get('cosi.rgwservice_port') != 'auto':
+ logging.info(f"*** configuration provided already a cosi.rgwservice_port={get('cosi.rgwservice_port')}")
+ detect_object_store_rgw_srv_port = False
+
+ if not detect_object_store and not detect_object_store_rgw_srv_name and not detect_object_store_rgw_srv_port:
+ return res
+
+ if detect_object_store:
+ storage_class = util.get_object_storage_class()
+ if storage_class:
+ res['cosi.bucket_storageclass'] = storage_class
+ _config['cosi.bucket_storageclass'] = storage_class
+
+ if detect_object_store_rgw_srv_name:
+ rgwsrv = util.get_object_storage_rgw_srv_name()
+ if(rgwsrv):
+ res['cosi.rgwservice_name'] = rgwsrv
+ _config['cosi.rgwservice_name'] = rgwsrv
+
+ if detect_object_store_rgw_srv_port:
+ rgwport = util.get_object_storage_rgw_srv_http_port()
+ if(rgwport):
+ res['cosi.rgwservice_port'] = rgwport
+ _config['cosi.rgwservice_port'] = rgwport
+ except:
+ pass
+ return res
+
def detect_env():
_config['operator.image'] = os.environ.get("OPERATOR_IMAGE", "missing-OPERATOR_IMAGE")
_config['operator.tag'] = os.environ.get("OPERATOR_TAG", "missing-OPERATOR_TAG")
@@ -195,6 +250,7 @@ def detect():
detect_storage()
detect_labels()
detect_env()
+ detect_object_storage()
def dump_config():
import nuvolaris.config as cfg
diff --git a/actions/devel/redis/nuvolaris/couchdb_util.py b/actions/devel/redis/nuvolaris/couchdb_util.py
index 3adb6b9..7a18899 100644
--- a/actions/devel/redis/nuvolaris/couchdb_util.py
+++ b/actions/devel/redis/nuvolaris/couchdb_util.py
@@ -127,6 +127,12 @@ class CouchDB:
r = self.db_session.put(url, data=data)
return r.status_code == 200
+ def enable_db_compaction(self,db_name):
+ url = f"{self.db_url}/_node/_local/_config/compactions/{self.db_prefix}{db_name}"
+ data = '[{db_fragmentation, \"60%\"}, {view_fragmentation, \"60%\"}]'
+ r = self.db_session.put(url, json=data)
+ return r.status_code == 200
+
def add_user(self, username: str, password: str):
userpass = {"name": username, "password": password, "roles": [], "type": "user"}
url = f"{self.db_url}/_users/org.couchdb.user:{username}"
diff --git a/actions/devel/upload/__main__.py b/actions/devel/upload/__main__.py
index 39b51d9..aee518d 100644
--- a/actions/devel/upload/__main__.py
+++ b/actions/devel/upload/__main__.py
@@ -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 os
import base64
import random
diff --git a/actions/devel/upload/nuvolaris/config.py b/actions/devel/upload/nuvolaris/config.py
index 787d4a7..283ab9f 100644
--- a/actions/devel/upload/nuvolaris/config.py
+++ b/actions/devel/upload/nuvolaris/config.py
@@ -174,6 +174,61 @@ def detect_storage(storages=None):
pass
return res
+def detect_object_storage(object_storages=None):
+ """
+ Detect the basic Object Store setup if enabled
+ """
+ res = {}
+ if not object_storages:
+ import nuvolaris.util as util
+
+ try:
+ detect_object_store = True
+ detect_object_store_rgw_srv_name = True
+ detect_object_store_rgw_srv_port = True
+
+ # skips autodetection if cephobjectstore is not active
+ if not exists('components.cosi') or not get('components.cosi'):
+ logging.info(f"*** cephobjectstore support is not requested skipping auto configuration check")
+ return res
+
+ # skips autodetection if already provided
+ if exists('cosi.bucket_storageclass') and get('cosi.bucket_storageclass') != 'auto':
+ logging.info(f"*** configuration provided already a cosi.bucket_storageclass={get('cosi.bucket_storageclass')}")
+ detect_object_store = False
+
+ if exists('cosi.rgwservice_name') and get('cosi.rgwservice_name') != 'auto':
+ logging.info(f"*** configuration provided already a cosi.rgwservice_name={get('cosi.rgwservice_name')}")
+ detect_object_store_rgw_srv_name = False
+
+ if exists('cosi.rgwservice_port') and get('cosi.rgwservice_port') != 'auto':
+ logging.info(f"*** configuration provided already a cosi.rgwservice_port={get('cosi.rgwservice_port')}")
+ detect_object_store_rgw_srv_port = False
+
+ if not detect_object_store and not detect_object_store_rgw_srv_name and not detect_object_store_rgw_srv_port:
+ return res
+
+ if detect_object_store:
+ storage_class = util.get_object_storage_class()
+ if storage_class:
+ res['cosi.bucket_storageclass'] = storage_class
+ _config['cosi.bucket_storageclass'] = storage_class
+
+ if detect_object_store_rgw_srv_name:
+ rgwsrv = util.get_object_storage_rgw_srv_name()
+ if(rgwsrv):
+ res['cosi.rgwservice_name'] = rgwsrv
+ _config['cosi.rgwservice_name'] = rgwsrv
+
+ if detect_object_store_rgw_srv_port:
+ rgwport = util.get_object_storage_rgw_srv_http_port()
+ if(rgwport):
+ res['cosi.rgwservice_port'] = rgwport
+ _config['cosi.rgwservice_port'] = rgwport
+ except:
+ pass
+ return res
+
def detect_env():
_config['operator.image'] = os.environ.get("OPERATOR_IMAGE", "missing-OPERATOR_IMAGE")
_config['operator.tag'] = os.environ.get("OPERATOR_TAG", "missing-OPERATOR_TAG")
@@ -195,6 +250,7 @@ def detect():
detect_storage()
detect_labels()
detect_env()
+ detect_object_storage()
def dump_config():
import nuvolaris.config as cfg
diff --git a/actions/devel/upload/nuvolaris/couchdb_util.py b/actions/devel/upload/nuvolaris/couchdb_util.py
index 3adb6b9..7a18899 100644
--- a/actions/devel/upload/nuvolaris/couchdb_util.py
+++ b/actions/devel/upload/nuvolaris/couchdb_util.py
@@ -127,6 +127,12 @@ class CouchDB:
r = self.db_session.put(url, data=data)
return r.status_code == 200
+ def enable_db_compaction(self,db_name):
+ url = f"{self.db_url}/_node/_local/_config/compactions/{self.db_prefix}{db_name}"
+ data = '[{db_fragmentation, \"60%\"}, {view_fragmentation, \"60%\"}]'
+ r = self.db_session.put(url, json=data)
+ return r.status_code == 200
+
def add_user(self, username: str, password: str):
userpass = {"name": username, "password": password, "roles": [], "type": "user"}
url = f"{self.db_url}/_users/org.couchdb.user:{username}"
diff --git a/deploy/nuvolaris-permissions/operator-clusterroles.yaml b/deploy/nuvolaris-permissions/operator-clusterroles.yaml
index e8721b4..eef4e9a 100644
--- a/deploy/nuvolaris-permissions/operator-clusterroles.yaml
+++ b/deploy/nuvolaris-permissions/operator-clusterroles.yaml
@@ -90,10 +90,6 @@ rules:
resources: ["deployments"]
verbs: ["get", "list", "watch"]
-# rook specific
-- apiGroups: ["ceph.rook.io"]
- resources: ["cephobjectstores"]
- verbs: ["get", "list", "watch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
diff --git a/deploy/nuvolaris-permissions/operator-roles.yaml b/deploy/nuvolaris-permissions/operator-roles.yaml
index d2f25b6..7c8f064 100644
--- a/deploy/nuvolaris-permissions/operator-roles.yaml
+++ b/deploy/nuvolaris-permissions/operator-roles.yaml
@@ -87,15 +87,6 @@ rules:
resources: ["kubegres","kubegres/status","kubegres/finalizers"]
verbs: ["get","patch","list","update","watch","create","delete"]
-# required for rook managed bucket under nuvolaris namespace
-- apiGroups: ["objectbucket.io"]
- resources: ["objectbucketclaims"]
- verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
-
-- apiGroups: ["ceph.rook.io"]
- resources: ["cephobjectstoreusers"]
- verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
-
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
diff --git a/deploy/nuvolaris-permissions/whisk-crd.yaml b/deploy/nuvolaris-permissions/whisk-crd.yaml
index e9ad9a7..d777d41 100644
--- a/deploy/nuvolaris-permissions/whisk-crd.yaml
+++ b/deploy/nuvolaris-permissions/whisk-crd.yaml
@@ -129,9 +129,6 @@ spec:
quota:
description: deploys the quota checker if supported by the operator
type: boolean
- cosi:
- description: activate the support for creating S3 compatible buckets using CephObjectStore bucket claims (will take precedence over MINIO if both are activated)
- type: boolean
required:
- openwhisk
- couchdb
@@ -688,40 +685,7 @@ spec:
- mem-req
- mem-lim
required:
- - containerPool
- cosi:
- description: used to configure the CEPH COS data storage service
- type: object
- properties:
- object_store_name:
- description: object store name. Defaults to nuvolaris-s3-store
- type: string
- bucket_storageclass:
- description: allow to set the a rook s3 storage class to be used when CephObjectStore is being used. Defaul to autoo which will force the operator to autodetect it
- type: string
- rgwservice_name:
- description: The CpehObjectStore RGW service name. Default auto which will force the operator to autodetect it
- type: string
- rgwservice_port:
- description: The CpehObjectStore RGW service port. Default auto which will force the operator to autodetect it
- type: string
- namespace:
- description: The namespace where Rook it is installed. Default to rook-ceph
- type: string
- ingress:
- description: configuration option for global minio ingresses exposure
- type: object
- properties:
- s3-enabled:
- description: boolean flag to activate CEPH S3 compatible ingress. Default to false
- type: boolean
- s3-hostname:
- description: ingress hostname to be used if true (normally s3.<apihost>)
- type: string
- required:
- - s3-enabled
- required:
- - ingress
+ - containerPool
status:
x-kubernetes-preserve-unknown-fields: true
# type: object
@@ -819,9 +783,4 @@ spec:
type: string
priority: 0
jsonPath: .status.whisk_create.static
- description: Static
- - name: Cos
- type: string
- priority: 0
- jsonPath: .status.whisk_create.cosi
- description: CephCos
\ No newline at end of file
+ description: Static
\ No newline at end of file
diff --git a/enterprise-util/faas/openwhisk-runtimes-cm.yaml b/enterprise-util/faas/openwhisk-runtimes-cm.yaml
deleted file mode 100644
index b14ee5a..0000000
--- a/enterprise-util/faas/openwhisk-runtimes-cm.yaml
+++ /dev/null
@@ -1,289 +0,0 @@
-# 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.
----
-apiVersion: v1
-kind: ConfigMap
-metadata:
- name: openwhisk-runtimes
- namespace: nuvolaris
-data:
- runtimes.json: |
- {
- "description": [
- "This file describes the different languages (aka. managed action runtimes) supported by the system",
- "as well as blackbox images that support the runtime specification.",
- "Only actions with runtime families / kinds defined here can be created / read / updated / deleted / invoked.",
- "Define a list of runtime families (example: 'nodejs') with at least one kind per family (example: 'nodejs:14').",
- "Each runtime family needs a default kind (default: true).",
- "When removing or renaming runtime families or runtime kinds from this file, preexisting actions",
- "with the affected kinds can no longer be read / updated / deleted / invoked. In order to remove or rename",
- "runtime families or runtime kinds, mark all affected runtime kinds as deprecated (deprecated: true) and",
- "perform a manual migration of all affected actions.",
- "",
- "This file is meant to list all stable runtimes supported by the Apache Openwhisk community."
- ],
- "runtimes": {
- "nodejs": [
- {
- "kind": "nodejs:18",
- "default": true,
- "image": {
- "prefix": "ghcr.io/nuvolaris",
- "name": "action-nodejs-v18",
- "tag": "0.3.0-dev.2305231724"
- },
- "deprecated": false,
- "attached": {
- "attachmentName": "codefile",
- "attachmentType": "text/plain"
- },
- "stemCells": [
- {
- "initialCount": 1,
- "memory": "256 MB",
- "reactive": {
- "minCount": 1,
- "maxCount": 4,
- "ttl": "2 minutes",
- "threshold": 1,
- "increment": 1
- }
- }
- ]
- },
- {
- "kind": "nodejs:16",
- "default": false,
- "image": {
- "prefix": "ghcr.io/nuvolaris",
- "name": "action-nodejs-v16",
- "tag": "0.3.0-dev.2305231724"
- },
- "deprecated": false,
- "attached": {
- "attachmentName": "codefile",
- "attachmentType": "text/plain"
- }
- }
- ],
- "python": [
- {
- "kind": "python:3",
- "default": true,
- "image": {
- "prefix": "ghcr.io/nuvolaris",
- "name": "action-python-v310",
- "tag": "0.3.0-morpheus.23012319"
- },
- "deprecated": false,
- "attached": {
- "attachmentName": "codefile",
- "attachmentType": "text/plain"
- },
- "stemCells": [
- {
- "initialCount": 1,
- "memory": "256 MB",
- "reactive": {
- "minCount": 1,
- "maxCount": 4,
- "ttl": "2 minutes",
- "threshold": 1,
- "increment": 1
- }
- }
- ]
- },
- {
- "kind": "python:310ai",
- "default": false,
- "image": {
- "prefix": "ghcr.io/nuvolaris",
- "name": "action-python-v310-ai",
- "tag": "0.3.0-morpheus.23030312"
- },
- "deprecated": false,
- "attached": {
- "attachmentName": "codefile",
- "attachmentType": "text/plain"
- }
- }
- ],
- "java": [
- {
- "kind": "java:8",
- "default": true,
- "image": {
- "prefix": "ghcr.io/nuvolaris",
- "name": "action-java-v8",
- "tag": "0.3.0-morpheus.22110809"
- },
- "deprecated": false,
- "attached": {
- "attachmentName": "codefile",
- "attachmentType": "text/plain"
- },
- "requireMain": true
- },
- {
- "kind": "java:17",
- "default": false,
- "image": {
- "prefix": "ghcr.io/it-change",
- "name": "java-v17",
- "tag": "20230629"
- },
- "deprecated": false,
- "attached": {
- "attachmentName": "codefile",
- "attachmentType": "text/plain"
- },
- "requireMain": true
- }
- ],
- "php": [
- {
- "kind": "php:7.3",
- "default": false,
- "deprecated": false,
- "image": {
- "prefix": "openwhisk",
- "name": "action-php-v7.3",
- "tag": "nightly"
- },
- "attached": {
- "attachmentName": "codefile",
- "attachmentType": "text/plain"
- }
- },
- {
- "kind": "php:7.4",
- "default": true,
- "deprecated": false,
- "image": {
- "prefix": "openwhisk",
- "name": "action-php-v7.4",
- "tag": "nightly"
- },
- "attached": {
- "attachmentName": "codefile",
- "attachmentType": "text/plain"
- }
- },
- {
- "kind": "php:8.0",
- "default": false,
- "deprecated": false,
- "image": {
- "prefix": "openwhisk",
- "name": "action-php-v8.0",
- "tag": "nightly"
- },
- "attached": {
- "attachmentName": "codefile",
- "attachmentType": "text/plain"
- }
- }
- ],
- "ruby": [
- {
- "kind": "ruby:2.5",
- "default": true,
- "deprecated": false,
- "attached": {
- "attachmentName": "codefile",
- "attachmentType": "text/plain"
- },
- "image": {
- "prefix": "openwhisk",
- "name": "action-ruby-v2.5",
- "tag": "nightly"
- }
- }
- ],
- "go": [
- {
- "kind": "go:1.17",
- "default": true,
- "deprecated": false,
- "attached": {
- "attachmentName": "codefile",
- "attachmentType": "text/plain"
- },
- "image": {
- "prefix": "openwhisk",
- "name": "action-golang-v1.17",
- "tag": "nightly"
- }
- }
- ],
- "dotnet": [
- {
- "kind": "dotnet:2.2",
- "default": true,
- "deprecated": false,
- "requireMain": true,
- "image": {
- "prefix": "openwhisk",
- "name": "action-dotnet-v2.2",
- "tag": "nightly"
- },
- "attached": {
- "attachmentName": "codefile",
- "attachmentType": "text/plain"
- }
- },
- {
- "kind": "dotnet:3.1",
- "default": false,
- "deprecated": false,
- "requireMain": true,
- "image": {
- "prefix": "openwhisk",
- "name": "action-dotnet-v3.1",
- "tag": "nightly"
- },
- "attached": {
- "attachmentName": "codefile",
- "attachmentType": "text/plain"
- }
- }
- ],
- "rust": [
- {
- "kind": "rust:1.34",
- "default": true,
- "image": {
- "prefix": "openwhisk",
- "name": "action-rust-v1.34",
- "tag": "nightly"
- },
- "deprecated": false,
- "attached": {
- "attachmentName": "codefile",
- "attachmentType": "text/plain"
- }
- }
- ]
- },
- "blackboxes": [
- {
- "prefix": "openwhisk",
- "name": "dockerskeleton",
- "tag": "nightly"
- }
- ]}
\ No newline at end of file
diff --git a/enterprise-util/faas/whisk_nuvorg.yaml b/enterprise-util/faas/whisk_nuvorg.yaml
deleted file mode 100644
index 76048ab..0000000
--- a/enterprise-util/faas/whisk_nuvorg.yaml
+++ /dev/null
@@ -1,152 +0,0 @@
-apiVersion: nuvolaris.org/v1
-kind: Whisk
-metadata:
- annotations:
- name: controller
- namespace: nuvolaris
-spec:
- components:
- couchdb: true
- cron: true
- invoker: true
- kafka: true
- minio: true
- mongodb: true
- monitoring: true
- openwhisk: true
- postgres: true
- redis: true
- static: true
- tls: true
- zookeeper: true
- configs:
- controller:
- javaOpts: -Xmx2048M
- loggingLevel: INFO
- resources:
- cpu-req: "500m"
- cpu-lim: "1"
- mem-req: "2G"
- mem-lim: "3G"
- invoker:
- containerPool:
- userMemory: 10240m
- javaOpts: -Xmx2048M
- loggingLevel: INFO
- resources:
- cpu-req: "500m"
- cpu-lim: "1"
- mem-req: "3G"
- mem-lim: "12G"
- limits:
- actions:
- invokes-concurrent: 2500
- invokes-perMinute: 9999
- sequence-maxLength: 500
- concurrency:
- limit-max: 8
- limit-min: 1
- limit-std: 1
- loadbalancer:
- blackbox-fraction: 10%
- timeout-factor: 2
- memory:
- limit-max: 2048m
- limit-min: 128m
- limit-std: 256m
- triggers:
- fires-perMinute: 5000
- controller:
- host: controller
- image: ghcr.io/nuvolaris/openwhisk-controller
- port: "3233"
- protocol: http
- tag: 0.3.0-morpheus.22122609
- couchdb:
- admin:
- password: some_passw0rd
- user: whisk_admin
- controller:
- password: s0meP@ass1
- user: invoker_admin
- host: couchdb
- invoker:
- password: s0meP@ass2
- user: controller_admin
- volume-size: 30
- invoker:
- host: invoker
- image: ghcr.io/nuvolaris/openwhisk-invoker
- port: "8080"
- protocol: http
- tag: 0.3.0-morpheus.22122609
- kafka:
- host: kafka
- volume-size: 30
- minio:
- admin:
- password: minioadmin
- user: minioadmin
- nuvolaris:
- password: zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG
- user: nuvolaris
- volume-size: 20
- mongodb:
- admin:
- password: 0therPa55
- user: whisk_admin
- exposedExternally: false
- host: mongodb
- nuvolaris:
- password: s0meP@ass3
- user: nuvolaris
- useOperator: false
- volume-size: 30
- monitoring:
- alert-manager:
- enabled: true
- gmail:
- default: false
- enabled: true
- from: ${ALERT_EMAIL_FROM}
- password: ${GMAIL_PASSWORD}
- to: ${ALERT_EMAIL_TO}
- username: ${GMAIL_USERNAME}
- slack:
- default: true
- enabled: true
- slack_api_url: ${SLACK_API_URL}
- slack_channel_name: '#monitoring-nuvolaris'
- volume-size: 2
- prometheus:
- volume-size: 20
- nuvolaris:
- apihost: nuvolaris.org
- openwhisk:
- namespaces:
- nuvolaris: cbd68075-dac2-475e-8c07-d62a30c7e683:123zO3xKCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP
- whisk-system: 789c46b1-71f6-4ed5-8c54-816aa4f8c502:abcfO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP
- postgres:
- admin:
- password: 0therPa55
- replica-password: 0therPa55RR
- nuvolaris:
- password: s0meP@ass3
- replicas: 2
- volume-size: 30
- redis:
- default:
- password: s0meP@ass3
- nuvolaris:
- password: s0meP@ass3
- prefix: nuv
- volume-size: 10
- scheduler:
- schedule: '* * * * *'
- tls:
- acme-registered-email: francesco@nuvolaris.io
- acme-server-url: https://acme-v02.api.letsencrypt.org/directory
- zookeeper:
- data-volume-size: 10
- host: zookeeper
- log-volume-size: 5
\ No newline at end of file
diff --git a/enterprise/.git-keep b/enterprise/.git-keep
deleted file mode 100644
index e69de29..0000000
diff --git a/enterprise-util/faas/.git-keep b/enterprise/.gitkeep
similarity index 100%
rename from enterprise-util/faas/.git-keep
rename to enterprise/.gitkeep
diff --git a/nuvolaris/ceph_cos.py b/nuvolaris/ceph_cos.py
deleted file mode 100644
index a80ea29..0000000
--- a/nuvolaris/ceph_cos.py
+++ /dev/null
@@ -1,346 +0,0 @@
-# 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 kopf, logging, os
-import nuvolaris.util as util
-import nuvolaris.openwhisk as openwhisk
-import nuvolaris.ceph_cos_ingress as ceph_ingress
-import nuvolaris.operator_util as operator_util
-import nuvolaris.apihost_util as apihost_util
-
-from nuvolaris.user_config import UserConfig
-from nuvolaris.user_metadata import UserMetadata
-from nuvolaris.ceph_cos_obc_data import CephObjectBucketClaimData
-from nuvolaris.ceph_cos_user_data import CephCosUserData
-from nuvolaris.s3_client import S3Client
-from nuvolaris.s3_bucket_policy import S3BucketPolicy, S3BucketStatement
-
-def _annotate_nuv_metadata(data):
- """
- annotate nuvolaris configmap with entries for ceph re-using MINIO_ENDPOINT, MINIO_PORT, MINIO_ACCESS_KEY, MINIO_SECRET_KEY
- for backward compatibility with MINIO based setup.
- """
- try:
- logging.info("annotating COS storage for nuvolaris")
- hostname, port = apihost_util.split_hostname_port(data['endpoint'])
-
- if hostname:
- openwhisk.annotate(f"minio_host={hostname}")
- openwhisk.annotate(f"s3_host={hostname}")
-
- if port:
- openwhisk.annotate(f"minio_port={port}")
- openwhisk.annotate(f"s3_port={port}")
-
- openwhisk.annotate(f"minio_access_key={data['access_key']}")
- openwhisk.annotate(f"minio_secret_key={data['secret_key']}")
- openwhisk.annotate(f"s3_access_key={data['access_key']}")
- openwhisk.annotate(f"s3_secret_key={data['secret_key']}")
- openwhisk.annotate(f"s3_provider=rook")
- except Exception as e:
- logging.error(f"failed to annotate COS storage for nuvolaris: {e}")
- return None
-
-def _add_cos_metadata(object_user, ucfg: UserConfig, user_metadata:UserMetadata):
- """
- adds entries for COS connectivity STORE_ENDPOINT, STORE_PORT, STORE_ACCESS_KEY, STORE_SECRET_KEY
- """
- try:
- hostname, port = apihost_util.split_hostname_port(util.get_object_storage_rgw_url())
- if(hostname):
- user_metadata.add_metadata("S3_PROVIDER","rook")
- user_metadata.add_metadata("MINIO_HOST",hostname)
- user_metadata.add_metadata("MINIO_ACCESS_KEY",object_user['access_key'])
- user_metadata.add_metadata("MINIO_SECRET_KEY",object_user['secret_key'])
- user_metadata.add_metadata("S3_HOST",hostname)
- user_metadata.add_metadata("S3_ACCESS_KEY",object_user['access_key'])
- user_metadata.add_metadata("S3_SECRET_KEY",object_user['secret_key'])
-
- if port:
- user_metadata.add_metadata("MINIO_PORT",port)
- user_metadata.add_metadata("S3_PORT",port)
- return None
- except Exception as e:
- logging.error("failed to add COSI metadara for %s: %s", ucfg.get('namespace'), e)
- return None
-
-def find_content_path(filename):
- """extract the absolute path of the given filename
-
- Args:
- filename (string): filename to be found
-
- Returns:
- string: absolute path
- """
- absolute_path = os.path.dirname(__file__)
- relative_path = "../deploy/content"
- return os.path.join(absolute_path, relative_path, filename)
-
-def create(owner=None):
- logging.info("*** configuring ceph object store support")
-
- data = util.get_cosi_config_data()
- res = ceph_ingress.create_cos_ingresses(data, owner)
-
- create_nuv_storage(data)
- logging.info("*** configured ceph object store")
- return res
-
-def build_rw_bucket_policy(bucket_name, username):
- """
- Builds RW bucket Statement
- """
- bkt_statement = S3BucketStatement()
- bkt_statement.with_allow()
- bkt_statement.with_aws_principal(f"arn:aws:iam:::user/{username}")
- bkt_statement.with_resource(f"arn:aws:s3:::{bucket_name}/*")
- bkt_statement.with_resource(f"arn:aws:s3:::{bucket_name}")
- return bkt_statement
-
-def build_public_bucket_policy(bucket_name):
- """
- Builds R bucket Statement
- """
- bkt_statement = S3BucketStatement()
- bkt_statement.with_allow()
- bkt_statement.with_all_principal()
- bkt_statement.with_s3_action("s3:GetObject")
- bkt_statement.with_resource(f"arn:aws:s3:::{bucket_name}/*")
- #bkt_statement.with_resource(f"arn:aws:s3:::{bucket_name}")
- return bkt_statement
-
-def build_public_list_bucket_policy(bucket_name):
- """
- Builds ListBucket bucket Statement to avoid 403 error when bucket resource does not exists and get a 404.
- """
- bkt_statement = S3BucketStatement()
- bkt_statement.with_allow()
- bkt_statement.with_all_principal()
- bkt_statement.with_s3_action("s3:ListBucket")
- #bkt_statement.with_resource(f"arn:aws:s3:::{bucket_name}/*")
- bkt_statement.with_resource(f"arn:aws:s3:::{bucket_name}")
- return bkt_statement
-
-def build_data_bucket_policy(bucket_name, username):
- """
- Builds bucket policy for the data bucket
- """
- bkt_policy = S3BucketPolicy()
- bkt_policy.with_statement(build_rw_bucket_policy(bucket_name, username))
- return bkt_policy
-
-def build_web_bucket_policy(bucket_name, username):
- """
- Builds bucket policy for the web bucket
- """
- bkt_policy = S3BucketPolicy()
- bkt_policy.with_statement(build_rw_bucket_policy(bucket_name, username))
- bkt_policy.with_statement(build_public_bucket_policy(bucket_name))
- bkt_policy.with_statement(build_public_list_bucket_policy(bucket_name))
- return bkt_policy
-
-def create_nuv_storage(data):
- """
- Creates nuvolaris Ceph Object Store custom resources
- """
- logging.info("*** configuring COS storage for nuvolaris")
- try:
- s3_host, s3_port = apihost_util.split_hostname_port(util.get_object_storage_rgw_url())
- ceph_object_user = CephCosUserData("nuvolaris")
- object_user = ceph_object_user.deploy()
-
- if object_user:
- logging.debug("created ceph object store user %s with endpoint %s", object_user['username'],object_user['endpoint'])
- _annotate_nuv_metadata(object_user)
-
- logging.info("*** adding nuvolaris S3 data bucket")
- obc_data = CephObjectBucketClaimData("nuvolaris-data")
- obc_data_result = obc_data.deploy()
-
- if 'bucket_name' in obc_data_result:
- # Set full access on the generated bucket name for the object_user
- data_policy : S3BucketPolicy = build_data_bucket_policy(obc_data_result['bucket_name'], object_user['username'])
- s3_client = S3Client(s3_host, s3_port,obc_data_result['access_key'],obc_data_result['secret_key'])
- s3_client.set_bucket_policy(obc_data_result['bucket_name'], data_policy)
- logging.info("granted full access to user %s on bucket %s", object_user['username'],obc_data_result['bucket_name'])
- openwhisk.annotate(f"minio_bucket_data={obc_data_result['bucket_name']}")
- openwhisk.annotate(f"s3_bucket_data={obc_data_result['bucket_name']}")
-
-
- logging.info("*** adding nuvolaris S3 static public bucket")
- obc_web = CephObjectBucketClaimData("nuvolaris-web")
- obc_web_result = obc_web.deploy()
-
- if 'bucket_name' in obc_web_result:
- # Set full access on the generated bucket name for the object_user
- web_policy = build_web_bucket_policy(obc_web_result['bucket_name'], object_user['username'] )
- s3_client = S3Client(s3_host, s3_port,obc_web_result['access_key'],obc_web_result['secret_key'])
- s3_client.set_bucket_policy(obc_web_result['bucket_name'], web_policy)
- logging.info("granted full access to user %s on bucket %s", object_user['username'],obc_data_result['bucket_name'])
- openwhisk.annotate(f"minio_bucket_static={obc_web_result['bucket_name']}")
- openwhisk.annotate(f"s3_bucket_static={obc_web_result['bucket_name']}")
-
- content_path = find_content_path("index.html")
-
- if(content_path):
- logging.info("uploading example content to %s from %s", obc_web_result['bucket_name'],content_path)
- s3_client.upload_file(content_path,obc_web_result['bucket_name'],"index.html")
- logging.info("uploading example content to nuvolaris-web from %s", content_path)
- else:
- logging.warning("could not find example static content to upload")
-
- logging.info(f"*** configured COS storage for nuvolaris")
- except Exception as e:
- logging.error("failed to configure COS storage for nuvolarus: %s",e)
- return None
-
-def create_ow_storage(state, ucfg: UserConfig, user_metadata: UserMetadata, owner=None):
- namespace = ucfg.get("namespace")
- s3_host, s3_port = apihost_util.split_hostname_port(util.get_object_storage_rgw_url())
- ceph_object_user = CephCosUserData(namespace)
-
- if ucfg.exists('object-storage.quota') and ucfg.get('object-storage.quota') not in ['auto']:
- ceph_object_user.with_quota_enabled(True)
- ceph_object_user.with_quota_limit(ucfg.get('object-storage.quota'))
-
- object_user = ceph_object_user.deploy()
-
- if object_user:
- logging.debug(f"created ceph object store user {object_user['username']} with endpoint {object_user['endpoint']}")
- _add_cos_metadata(object_user, ucfg, user_metadata)
- state['storage_user']='on'
-
- logging.info(f"*** configuring COSI storage for namespace {namespace}")
-
- if(ucfg.get('object-storage.data.enabled')) and 'username' in object_user:
- bucket_name = ucfg.get('object-storage.data.bucket')
- logging.info(f"*** adding private bucket {bucket_name} for {namespace}")
-
- obc_data = CephObjectBucketClaimData(bucket_name)
- obc_data_result = obc_data.deploy()
-
- if 'bucket_name' in obc_data_result:
- # Set full access on the generated bucket name for the object_user
- data_policy : S3BucketPolicy = build_data_bucket_policy(obc_data_result['bucket_name'], object_user['username'])
- s3_client = S3Client(s3_host, s3_port,obc_data_result['access_key'],obc_data_result['secret_key'])
- s3_client.set_bucket_policy(obc_data_result['bucket_name'], data_policy)
- user_metadata.add_metadata("MINIO_DATA_BUCKET",obc_data_result['bucket_name'])
- user_metadata.add_metadata("S3_DATA_BUCKET",obc_data_result['bucket_name'])
- logging.info("granted full access to user %s on bucket %s", object_user['username'],obc_data_result['bucket_name'])
-
- if(ucfg.get('object-storage.route.enabled')) and 'username' in object_user:
- bucket_name = ucfg.get("object-storage.route.bucket")
- logging.info("*** adding public bucket %s for %s", bucket_name, namespace)
-
- obc_web = CephObjectBucketClaimData(bucket_name)
- obc_web_result = obc_web.deploy()
-
- if 'bucket_name' in obc_web_result:
- # Set full access on the generated bucket name for the object_user
- web_policy = build_web_bucket_policy(obc_web_result['bucket_name'], object_user['username'] )
- s3_client = S3Client(s3_host, s3_port,obc_web_result['access_key'],obc_web_result['secret_key'])
- s3_client.set_bucket_policy(obc_web_result['bucket_name'], web_policy)
- user_metadata.add_metadata("MINIO_BUCKET_STATIC",obc_web_result['bucket_name'])
- user_metadata.add_metadata("S3_BUCKET_STATIC",obc_web_result['bucket_name'])
- ucfg.put("S3_BUCKET_STATIC",obc_web_result['bucket_name'])
- logging.info("granted full access to user %s on bucket %s",object_user['username'],obc_data_result['bucket_name'])
-
- content_path = find_content_path("index.html")
-
- if(content_path):
- logging.info("uploading example content to %s from %s",obc_web_result['bucket_name'],content_path)
- s3_client.upload_file(content_path,obc_web_result['bucket_name'],"index.html")
- logging.info("uploaded example content to %s from %s",obc_web_result['bucket_name'],content_path)
- else:
- logging.error("could not find example static content to upload")
-
- state['storage_resource']='on'
- return state
-
-def delete_ow_storage(ucfg: UserConfig):
- """Delete COSI based bucket resources for the gien wsku user
-
- Args:
- ucfg (UserConfig): User Configuration Data
-
- Returns:
- bool: True if the deletion is completed
- """
- try:
- namespace = ucfg.get("namespace")
- ceph_object_user = CephCosUserData(namespace)
- ceph_object_user.undeploy()
-
- if(ucfg.get('object-storage.data.enabled')):
- bucket_name = ucfg.get('object-storage.data.bucket')
- logging.info("*** removing private bucket %s for %s", bucket_name, namespace)
- obc = CephObjectBucketClaimData(bucket_name)
- obc.undeploy()
-
- if(ucfg.get('object-storage.route.enabled')):
- bucket_name = ucfg.get("object-storage.route.bucket")
- logging.info("*** removing public bucket %s for %s", bucket_name, namespace)
- obc = CephObjectBucketClaimData(bucket_name)
- obc.undeploy()
- return True
- except Exception as e:
- logging.error("error deleting user for namespace %s, %s", ucfg.get("namespace"), e)
- return False
-
-def delete(owner=None):
- try:
- data = util.get_cosi_config_data()
- ceph_ingress.delete_cos_ingresses(data, owner)
- except Exception as e:
- logging.error('*** failed to delete cosi: %s' % e)
-
-def patch(status, action, owner=None):
- """
- Called by the operator patcher to create/delete minio component
- """
- try:
- logging.info(f"*** handling request to {action} cosi")
- if action == 'create':
- msg = create(owner)
- operator_util.patch_operator_status(status,'cosi','on')
- else:
- delete(owner)
- operator_util.patch_operator_status(status,'cosi','off')
-
- logging.info(msg)
- logging.info(f"*** hanlded request to {action} cosi")
- except Exception as e:
- logging.error('*** failed to update cosi: %s' % e)
- operator_util.patch_operator_status(status,'cosi','error')
-
-def patch_ingresses(status, action, owner=None):
- """
- Called by the operator patcher to create/delete minio component
- """
- try:
- logging.info(f"*** handling request to {action} cosi ingresses")
- data = util.get_cosi_config_data()
- if action == 'update':
- msg = ceph_ingress.create_cos_ingresses(data, owner)
- operator_util.patch_operator_status(status,'cosi-ingresses','on')
-
- logging.info(msg)
- logging.info(f"*** handled request to {action} cosi ingresses")
- except Exception as e:
- logging.error('*** failed to update cosi ingresses: %s' % e)
- operator_util.patch_operator_status(status,'cosi-ingresses','error')
diff --git a/nuvolaris/ceph_cos_ingress.py b/nuvolaris/ceph_cos_ingress.py
deleted file mode 100644
index 5310f58..0000000
--- a/nuvolaris/ceph_cos_ingress.py
+++ /dev/null
@@ -1,150 +0,0 @@
-# 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 kopf, logging, time, os
-import nuvolaris.kube as kube
-import nuvolaris.kustomize as kus
-import nuvolaris.config as cfg
-import nuvolaris.util as util
-import nuvolaris.apihost_util as apihost_util
-import nuvolaris.endpoint as endpoint
-
-from nuvolaris.ingress_data import IngressData
-from nuvolaris.route_data import RouteData
-
-def deploy_cos_route(apihost,namespace,type,service_name,port,context_path):
- """
- Deploys a generic CEPH COS route ingress under the rook-ceph namespace
- param: apihost
- param: namespace
- param: type (s3)
- param: service_name (normally it is the RGW hostname)
- param: port (80)
- paramL context_path (/)
- """
- route = RouteData(apihost)
- route.with_route_name(endpoint.api_route_name(namespace,type))
- route.with_service_name(service_name)
- route.with_service_kind("Service")
- route.with_service_port(port)
- route.with_context_path(context_path)
- route.with_namespace(namespace)
-
- logging.info(f"*** configuring CEPH cos route for service {service_name}:{port}")
- path_to_template_yaml = route.render_template(namespace)
- res = kube.kubectl("apply", "-f",path_to_template_yaml,namespace=namespace)
- os.remove(path_to_template_yaml)
- return res
-
-def deploy_cos_ingress(apihost, namespace, type, service_name,port,context_path):
- """
- Deploys a generic CEPH COS nginx/traefik ingress under the rook-ceph namespace
- param: apihost
- param: namespace
- param: type (s3, console)
- param: service_name (normally it is the RGW hostname)
- param: port (9000 or 9090)
- paramL context_path (/)
- """
- ingress = IngressData(apihost)
- ingress.with_ingress_name(endpoint.api_ingress_name(namespace, type))
- ingress.with_secret_name(endpoint.ingress_secret_name(namespace, type))
- ingress.with_context_path(context_path)
- ingress.with_service_name(service_name)
- ingress.with_service_port(port)
- ingress.with_namespace(namespace)
-
- if ingress.requires_traefik_middleware():
- logging.info(f"*** configuring traefik middleware for {type} ingress")
- path_to_template_yaml = ingress.render_traefik_middleware_template(namespace)
- res = kube.kubectl("apply", "-f",path_to_template_yaml, namespace=namespace)
- os.remove(path_to_template_yaml)
-
- logging.info(f"*** configuring static ingress for {type}")
- path_to_template_yaml = ingress.render_template(namespace)
- res = kube.kubectl("apply", "-f",path_to_template_yaml,namespace=namespace)
- os.remove(path_to_template_yaml)
-
- return res
-
-def create_s3_ingress_endpoint(data, runtime, apihost,service_name="rook-ceph-rgw-nuvolaris-s3-store", service_port=80, owner=None):
- """
- exposes CEPH OBJECT STORE S3 api ingress ingress/route
- """
- if runtime == 'openshift':
- return deploy_cos_route(apihost,data['cluster_namespace'],"cos-s3", service_name, service_port,"/")
- else:
- return deploy_cos_ingress(apihost,data['cluster_namespace'],"cos-s3",service_name, service_port,"/")
-
-def get_cos_ingress_hostname(runtime, apihost_url, prefix, hostname_from_config):
- """
- Determine CEPH COS ingress hostname. In auto mode the prefix is appended
- to the configured apihost,, otherwise the one from configuration is used.
- """
- if hostname_from_config in ["auto"]:
- return apihost_util.append_prefix_to_url(apihost_url, prefix)
-
- return apihost_util.get_ingress_url(runtime, hostname_from_config)
-
-
-def create_cos_ingresses(data, owner=None):
- """
- Creates all the Ceph Object related ingresses according to provided configuration
- """
- runtime = cfg.get('nuvolaris.kube')
- apihost_url = apihost_util.get_apihost(runtime)
- res = ""
-
- if data['s3_ingress_enabled']:
- s3_hostname_url = get_cos_ingress_hostname(runtime, apihost_url,"s3",data['s3_ingress_hostname'])
- res += create_s3_ingress_endpoint(data, runtime, s3_hostname_url,data['rgwservice_name'],data['rgwservice_port'], owner)
-
- return res
-
-
-def delete_cos_ingress(runtime, namespace, ingress_class, type, owner=None):
- """
- undeploys ingresses for CEPH COS
- """
- logging.info(f"*** removing ingresses for CEPH COS")
-
- try:
- res = ""
- if(runtime=='openshift'):
- res = kube.kubectl("delete", "route",endpoint.api_route_name(namespace,type), namespace="rook-ceph")
- return res
-
- res += kube.kubectl("delete", "ingress",endpoint.api_ingress_name(namespace,type), namespace="rook-ceph")
-
- if(ingress_class == 'traefik'):
- res = kube.kubectl("delete", "middleware.traefik.containo.us",endpoint.api_middleware_ingress_name(namespace,type), namespace="rook-ceph")
-
- return res
- except Exception as e:
- logging.warn(e)
- return None
-
-def delete_cos_ingresses(data, owner=None):
- namespace = "nuvolaris"
- runtime = cfg.get('nuvolaris.kube')
- ingress_class = util.get_ingress_class(runtime)
-
- if data['cos_s3_ingress_enabled']:
- res += delete_cos_ingress(runtime, namespace, ingress_class, "cos-s3", owner)
-
-
- return res
\ No newline at end of file
diff --git a/nuvolaris/ceph_cos_obc_data.py b/nuvolaris/ceph_cos_obc_data.py
deleted file mode 100644
index 639adda..0000000
--- a/nuvolaris/ceph_cos_obc_data.py
+++ /dev/null
@@ -1,134 +0,0 @@
-# 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 json
-import logging
-import os
-import nuvolaris.config as cfg
-import nuvolaris.util as util
-import nuvolaris.kustomize as kus
-import nuvolaris.time_util as tutil
-import nuvolaris.template as ntp
-import urllib.parse
-import nuvolaris.kube as kube
-
-from nuvolaris.util import nuv_retry
-
-class CephObjectBucketClaimData:
- _data = {}
-
- def __init__(self, bucket_name):
- self._data = util.get_cosi_config_data()
- self._data['bucket_name'] = bucket_name
-
- def dump(self):
- logging.debug(json.dumps(self._data))
-
- def with_quota_enabled(self,value: bool):
- self._data['quota_enabled']=value
-
- def with_quota_limit(self,value: str):
- self._data['quota_limit']=value
-
- def build_spec(self, where: str, out_template, tpl = "cosi-obc-tpl.yaml"):
- logging.info(f"*** Building ObjectBucketClaim template for bucket {self._data['bucket_name']} via template {tpl}")
- return kus.processTemplate(where, tpl, self._data, out_template)
-
- def render_template(self,tpl = "cosi-obc-tpl.yaml"):
- """
- uses the given template to render an ObjectBucketClaim template and returns the path to the template
- """
- out = f"/tmp/__{self._data['bucket_name']}_{tpl}"
- file = ntp.spool_template(tpl, out, self._data)
- return os.path.abspath(file)
-
- def _get_status(self):
- statuses = kube.kubectl("get", f"objectbucketclaim/{self._data['bucket_name']}", jsonpath="{.status}")
- if statuses and len(statuses) > 0:
- return statuses[0]
- return None
-
- @nuv_retry()
- def wait_for_bucket_spec(self):
- """
- Wait for the ObjectBucketClaim to become bound and return the generated bucketName
- return the ObjectBucketClaim generated bucketName
- """
- status = self._get_status()
- if('phase' in status and status['phase'] in ['Bound']):
- bucket_names = kube.kubectl("get", f"objectbucketclaim/{self._data['bucket_name']}", jsonpath="{.spec.bucketName}")
- return bucket_names[0]
-
- raise Exception(f"objectbucketclaim/{self._data['bucket_name']} not bound yet")
-
- @nuv_retry()
- def _get_secret(self, secret_name):
- secrets = kube.kubectl("get", f"secret/{secret_name}", jsonpath="{.data}")
- if secrets and len(secrets) > 0:
- return secrets[0]
-
- raise Exception(f"secret/{secret_name} not found yet")
-
- def extract_obc_secret_data(self, generated_bucket_name):
- """
- Extract data from a secret generated when Rook deploys a ObjectBucketClaim. The secret contains a data structure with these values
- apiVersion: v1
- Kind: Secret
- data:
- AWS_ACCESS_KEY_ID: QzVLSFdCVlZHOEEyTlk0RElUVEg=
- AWS_SECRET_ACCESS_KEY: V0FiTGNIa3JPMUxqSjFKcjRmTGtkYnRCa0NCdkI3SWFDSEVUeVIzdA==
- return: dictionary containing {bucket_name, access_key, secret_key}
- """
- secret = self._get_secret(self._data['bucket_name'])
-
- return {
- "bucket_name":generated_bucket_name,
- "access_key":util.b64_decode(secret['AWS_ACCESS_KEY_ID']),
- "secret_key":util.b64_decode(secret['AWS_SECRET_ACCESS_KEY'])
- }
-
- def deploy(self):
- """
- Build a ObjectBucketClaim template and deploys it. Check that the ObjectBucketClaim becomes Bound
- and extract the information about the ObjectBucketClaim secrets
- return: dictionary containing {bucket_name, access_key, secret_key} attributes
- """
- try:
- logging.info(f"*** deploying ObjectBucketClaim {self._data['bucket_name']}")
- path_to_template_yaml = self.render_template()
- kube.kubectl("apply", "-f",path_to_template_yaml)
- os.remove(path_to_template_yaml)
- logging.info(f"*** ObjectBucketClaim {self._data['bucket_name']} deployed successfully.")
-
- generated_bucket_name = self.wait_for_bucket_spec()
- logging.info(f"generated bucket name {generated_bucket_name} for objectbucketclaim/{self._data['bucket_name']}. Extracting data")
- return self.extract_obc_secret_data(generated_bucket_name)
- except Exception as e:
- logging.warn(e)
- return None
-
- def undeploy(self):
- """
- Removes the ObjectBucketClaim
- """
- try:
- logging.info(f"*** undeploying ObjectBucketClaim {self._data['bucket_name']}")
- kube.kubectl("delete", f"objectbucketclaim/{self._data['bucket_name']}")
- return True
- except Exception as e:
- logging.warn(e)
- return False
\ No newline at end of file
diff --git a/nuvolaris/ceph_cos_user_data.py b/nuvolaris/ceph_cos_user_data.py
deleted file mode 100644
index d2c527b..0000000
--- a/nuvolaris/ceph_cos_user_data.py
+++ /dev/null
@@ -1,137 +0,0 @@
-# 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 json
-import logging
-import os
-import nuvolaris.config as cfg
-import nuvolaris.util as util
-import nuvolaris.kustomize as kus
-import nuvolaris.time_util as tutil
-import nuvolaris.template as ntp
-import urllib.parse
-import nuvolaris.kube as kube
-
-from nuvolaris.util import nuv_retry
-
-class CephCosUserData:
- _data = {}
-
- def __init__(self, username):
- self._data = util.get_cosi_config_data()
- self._data['username'] = username
-
- def dump(self):
- logging.debug(json.dumps(self._data))
-
- def with_quota_enabled(self,value: bool):
- self._data['quota_enabled']=value
-
- def with_quota_limit(self,value: str):
- self._data['quota_limit']=value
-
- def with_bucket_limit(self,value: int):
- self._data['max_bucket_limit']=value
-
- def build_spec(self, where: str, out_template, tpl = "cosi-user-tpl.yaml"):
- logging.info(f"*** Building CephObjectStoreUser template for user {self._data['username']} via template {tpl}")
- return kus.processTemplate(where, tpl, self._data, out_template)
-
- def render_template(self,tpl="cosi-user-tpl.yaml"):
- """
- uses the given template to render a CephObjectStoreUser template and returns the path to the template
- """
- out = f"/tmp/__{self._data['username']}_{tpl}"
- file = ntp.spool_template(tpl, out, self._data)
- return os.path.abspath(file)
-
- def _get_status(self):
- statuses = kube.kubectl("get", f"cephobjectstoreusers/{self._data['username']}", jsonpath="{.status}")
- if statuses and len(statuses) > 0:
- return statuses[0]
- return None
-
- @nuv_retry()
- def _wait_for_secret_name(self):
- """
- Wait for the cephobjectstoreuser to become ready and return the generated secretName
- return the cephobjectstoreuser generated secret Name
- """
- status = self._get_status()
- if('phase' in status and status['phase'] in ['Ready']):
- return status['info']['secretName']
-
- raise Exception(f"cephobjectstoreusers/{self._data['username']} not ready yet")
-
- @nuv_retry()
- def _get_secret(self, secret_name):
- secrets = kube.kubectl("get", f"secret/{secret_name}", jsonpath="{.data}")
- if secrets and len(secrets) > 0:
- return secrets[0]
-
- raise Exception(f"secret/{secret_name} not found yet")
-
- def _extract_cos_user_secret_data(self, secret_name):
- """
- Extract data from a secret generated when Rook deploys a CephObjectStoreUser. The secret contains a data structure with these values
- apiVersion: v1
- Kind: Secret
- data:
- AccessKey: VkIyRjU5WTZXREVZWlQyUTZJMEc=
- Endpoint: aHR0cDovL3Jvb2stY2VwaC1yZ3ctbnV2b2xhcmlzLXMzLXN0b3JlLnJvb2stY2VwaC5zdmM6ODA=
- SecretKey: M2g2QVRCOXRERFVDcURpWnpmb3hTcWhuY25HS0IzSTQ3OTlCNkZ4bw==
- return: dictionary containing {username, access_key, secret_key, endpoint}
- """
- data = self._get_secret(secret_name)
- return {
- "username":self._data['username'],
- "access_key":util.b64_decode(data['AccessKey']),
- "secret_key":util.b64_decode(data['SecretKey']),
- "endpoint":util.b64_decode(data['Endpoint'])
- }
-
- def deploy(self):
- """
- Build a CephObjectStoreUser template and deploys it. Check that the CephObjectStoreUser user becomes Ready
- and extract the information about the User secrets
- return: dictionary containing {username, access_key, secret_key, endpoint} attributes
- """
- try:
- logging.info(f"*** deploying CephObjectStoreUser {self._data['username']}")
- path_to_template_yaml = self.render_template()
- kube.kubectl("apply", "-f",path_to_template_yaml)
- os.remove(path_to_template_yaml)
- logging.info(f"*** CephObjectStoreUser {self._data['username']} deployed successfully.")
-
- secret_name = self._wait_for_secret_name()
- logging.info(f"found secret {secret_name} for cephobjectstoreusers/{self._data['username']}. Extracting data")
- return self._extract_cos_user_secret_data(secret_name)
- except Exception as e:
- logging.warn(e)
- return None
-
- def undeploy(self):
- """
- Removes the CephObjectStoreUser
- """
- try:
- logging.info(f"*** undeploying CephObjectStoreUser {self._data['username']}")
- kube.kubectl("delete", f"cephobjectstoreusers/{self._data['username']}")
- return True
- except Exception as e:
- logging.warn(e)
- return False
\ No newline at end of file
diff --git a/nuvolaris/kopf_util.py b/nuvolaris/kopf_util.py
index 462a849..d59cd33 100644
--- a/nuvolaris/kopf_util.py
+++ b/nuvolaris/kopf_util.py
@@ -73,14 +73,6 @@ def check_minio_ingresses(response: dict, item: dict):
if(item['new']):
response["minio-ingresses"]="update"
-def check_cosi_ingresses(response: dict, item: dict):
- """
- Forces an update of cosi-ingresses if needed
- """
- if item['path']=='spec.cosi.ingress.s3-enabled':
- if(item['new']):
- response["cosi-ingresses"]="update"
-
def evaluate_differences(response: dict, differences: list):
"""
Iterate over the difference list to find which components the
@@ -100,7 +92,6 @@ def evaluate_differences(response: dict, differences: list):
openwhisk(response, d)
endpoint(response, d)
check_minio_ingresses(response, d)
- check_cosi_ingresses(response,d)
def detect_component_changes(kopf_diff):
"""
diff --git a/nuvolaris/main.py b/nuvolaris/main.py
index 69000d8..19f74df 100644
--- a/nuvolaris/main.py
+++ b/nuvolaris/main.py
@@ -39,7 +39,6 @@ import nuvolaris.postgres_operator as postgres
import nuvolaris.runtimes_preloader as preloader
import nuvolaris.monitoring as monitoring
import nuvolaris.quota_checker_job as quota
-import nuvolaris.ceph_cos as cosi
@kopf.on.startup()
def configure(settings: kopf.OperatorSettings, **_):
@@ -78,14 +77,9 @@ def whisk_create(spec, name, **kwargs):
"minio": "?", # Minio configuration
"static": "?", # Minio static endpoint provider
"zookeeper": "?", #Zookeeper configuration
- "quota":"?" ,#Quota configuration
- "cosi":"?" #Ceph Object Store support active
+ "quota":"?" #Quota configuration
}
- if cfg.get('components.minio') and cfg.get('components.cosi'):
- state['controller']= "NotValid"
- raise kopf.PermanentError("Storage support for MINIO and CEPH BUCKET could not be activated simultaneously.")
-
runtime = cfg.get('nuvolaris.kube')
logging.info(f"kubernetes engine in use={runtime}")
@@ -156,13 +150,6 @@ def whisk_create(spec, name, **kwargs):
else:
state['minio'] = "off"
- if cfg.get('components.cosi'):
- msg = cosi.create(owner)
- logging.info(msg)
- state['cosi'] = "on"
- else:
- state['cosi'] = "off"
-
if cfg.get('components.static'):
msg = static.create(owner)
logging.info(msg)
@@ -308,10 +295,6 @@ def whisk_delete(spec, **kwargs):
msg = minio.delete()
logging.info(msg)
- if cfg.get("components.cosi"):
- msg = cosi.delete()
- logging.info(msg)
-
if cfg.get('components.postgres'):
msg = postgres.delete()
logging.info(msg)
diff --git a/nuvolaris/patcher.py b/nuvolaris/patcher.py
index d8454fe..db72a51 100644
--- a/nuvolaris/patcher.py
+++ b/nuvolaris/patcher.py
@@ -33,7 +33,6 @@ import nuvolaris.operator_util as operator_util
import nuvolaris.runtimes_preloader as preloader
import nuvolaris.invoker as invoker
import nuvolaris.quota_checker_job as quota
-import nuvolaris.ceph_cos as cosi
def patch_preloader(owner: None):
try:
@@ -142,10 +141,6 @@ def patch(diff, status, owner=None, name=None):
minio.patch(status,what_to_do['minio'], owner)
components_updated = True
- if "cosi" in what_to_do:
- cosi.patch(status,what_to_do['cosi'], owner)
- components_updated = True
-
if "static" in what_to_do:
static.patch(status,what_to_do['static'], owner)
components_updated = True
@@ -167,9 +162,6 @@ def patch(diff, status, owner=None, name=None):
if "minio-ingresses" in what_to_do and what_to_do['minio-ingresses'] == "update":
minio.patch_ingresses(status,what_to_do['minio-ingresses'], owner)
- if "cosi-ingresses" in what_to_do and what_to_do['cosi-ingresses'] == "update":
- cosi.patch_ingresses(status,what_to_do['minio-ingresses'], owner)
-
if components_updated:
operator_util.whisk_post_create(name)
diff --git a/nuvolaris/templates/redis_quota_checker.lua b/nuvolaris/templates/redis_quota_checker.lua
index 6cefa04..58ec0a2 100644
--- a/nuvolaris/templates/redis_quota_checker.lua
+++ b/nuvolaris/templates/redis_quota_checker.lua
@@ -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.
+--
-- Define the key prefix to search for
local prefix = ARGV[1]
local cursor = 0
diff --git a/nuvolaris/templates/redis_quota_checker.sh b/nuvolaris/templates/redis_quota_checker.sh
index cb0d13b..f2fd18b 100644
--- a/nuvolaris/templates/redis_quota_checker.sh
+++ b/nuvolaris/templates/redis_quota_checker.sh
@@ -1,4 +1,20 @@
#!/bin/bash
+#
+# 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.
+#
export REDISCLI_AUTH="{{redis_password}}"
export SCRIPT=$(redis-cli SCRIPT LOAD "$(cat {{path_to_lua_script}})")
redis-cli EVALSHA $SCRIPT 0 $1
diff --git a/nuvolaris/user_handlers.py b/nuvolaris/user_handlers.py
index d81c30a..1a18da6 100644
--- a/nuvolaris/user_handlers.py
+++ b/nuvolaris/user_handlers.py
@@ -30,7 +30,6 @@ import nuvolaris.userdb_util as userdb
import nuvolaris.postgres_operator as postgres
import nuvolaris.endpoint as endpoint
import nuvolaris.user_patcher as user_patcher
-import nuvolaris.ceph_cos as cosi
from nuvolaris.user_config import UserConfig
from nuvolaris.user_metadata import UserMetadata
@@ -70,10 +69,7 @@ def whisk_user_create(spec, name, patch, **kwargs):
if(cfg.get('components.minio') and (ucfg.get('object-storage.data.enabled') or ucfg.get('object-storage.route.enabled'))):
minio_deploy.create_ow_storage(state, ucfg, user_metadata, owner)
- if(cfg.get('components.cosi') and (ucfg.get('object-storage.data.enabled') or ucfg.get('object-storage.route.enabled'))):
- cosi.create_ow_storage(state, ucfg, user_metadata, owner)
-
- if((cfg.get('components.minio') or cfg.get('components.cosi')) and ucfg.get('object-storage.route.enabled') and cfg.get('components.static')):
+ if(cfg.get('components.minio') and ucfg.get('object-storage.route.enabled') and cfg.get('components.static')):
res = static.create_ow_static_endpoint(ucfg,user_metadata, owner)
logging.info("OpenWhisk static endpoint for %s added = %s", ucfg.get('namespace'), res)
state['static']= res
@@ -123,10 +119,6 @@ def whisk_user_delete(spec, name, **kwargs):
res = minio_deploy.delete_ow_storage(ucfg)
logging.info(f"OpenWhisk namespace {ucfg.get('namespace')} MINIO storage removed = {res}")
- if(cfg.get('components.cosi') and (ucfg.get('object-storage.data.enabled') or ucfg.get('object-storage.route.enabled'))):
- res = cosi.delete_ow_storage(ucfg)
- logging.info(f"OpenWhisk namespace {ucfg.get('namespace')} COSI storage removed = {res}")
-
if(cfg.get('components.minio') and ucfg.get('object-storage.route.enabled') and cfg.get('components.static')):
res = static.delete_ow_static_endpoint(ucfg)
logging.info(f"OpenWhisk static endpoint for {ucfg.get('namespace')} removed = {res}")
diff --git a/tests/k3s/whisk-demo-user.yaml b/tests/k3s/whisk-demo-user.yaml
deleted file mode 100644
index ba45ba5..0000000
--- a/tests/k3s/whisk-demo-user.yaml
+++ /dev/null
@@ -1,47 +0,0 @@
-# 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.
-#
-apiVersion: nuvolaris.org/v1
-kind: WhiskUser
-metadata:
- name: demo-postgres-user
- namespace: nuvolaris
-spec:
- auth: ba7e4790-444a-4094-8fe4-1ec2ad64931c:mdKKByEJQrR7wQrVkZirfGatwcKCSyuvBo5wyFMXZYMUtCfalk3yWLamkDsfFxUX
- email: demo-postgres-user@email.com
- mongodb:
- database: demo-postgres-user
- enabled: false
- password: bD5F3RfA4NgR
- namespace: demo-postgres-user
- object-storage:
- data:
- bucket: demo-postgres-user-data
- enabled: false
- password: ugzGtF0005WH8o7nYgJuihze7XCgnPuc8b1cb7Rn
- route:
- bucket: demo-postgres-user-web
- enabled: false
- password: lZA6d8GHizTc
- postgres:
- database: demo-postgres-user
- enabled: true
- password: ief2mvpcWlTd
- redis:
- enabled: false
- password: 4pd77bowP38s
- prefix: demo-postgres-user
\ No newline at end of file
diff --git a/tests/k3s/whisk-full-arm.yaml b/tests/k3s/whisk-full-arm.yaml
deleted file mode 100644
index f449242..0000000
--- a/tests/k3s/whisk-full-arm.yaml
+++ /dev/null
@@ -1,172 +0,0 @@
-# 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.
-#
-apiVersion: nuvolaris.org/v1
-kind: Whisk
-metadata:
- name: controller
- namespace: nuvolaris
-spec:
- nuvolaris:
- password: nuvpassw0rd
- apihost: api.arm.nuvtest.net
- components:
- # start openwhisk controller
- openwhisk: true
- # start openwhisk invoker
- invoker: true
- # start couchdb
- couchdb: true
- # start kafka
- kafka: true
- # start mongodb
- mongodb: true
- # start redis
- redis: true
- # start cron based action parser
- cron: true
- # enable TLS
- tls: true
- # minio enabled or not
- minio: true
- # zookeeper enabled or not
- zookeeper: true
- # minio static enabled or not
- static: true
- # monitoring enabled or not
- monitoring: true
- # postgres enabled or not
- postgres: true
- openwhisk:
- namespaces:
- whisk-system: 789c46b1-71f6-4ed5-8c54-816aa4f8c502:abcfO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP
- nuvolaris: cbd68075-dac2-475e-8c07-d62a30c7e683:123zO3xKCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP
- couchdb:
- host: couchdb
- volume-size: 30
- admin:
- user: whisk_admin
- password: some_passw0rd
- controller:
- user: invoker_admin
- password: s0meP@ass1
- invoker:
- user: controller_admin
- password: s0meP@ass2
- kafka:
- host: kafka
- volume-size: 30
- controller:
- protocol: "http"
- host : "controller"
- port: "3233"
- image: "ghcr.io/nuvolaris/openwhisk-controller"
- tag: "0.3.0-morpheus.22122609"
- invoker:
- protocol: "http"
- host : "invoker"
- port: "8080"
- image: "ghcr.io/nuvolaris/openwhisk-invoker"
- tag: "0.3.0-morpheus.22122609"
- scheduler:
- schedule: "* * * * *"
- tls:
- acme-registered-email: francesco@nuvolaris.io
- acme-server-url: https://acme-v02.api.letsencrypt.org/directory
- zookeeper:
- host: zookeeper
- data-volume-size: 10
- log-volume-size: 5
- configs:
- limits:
- actions:
- sequence-maxLength: 500
- invokes-perMinute: 9999
- invokes-concurrent: 2500
- triggers:
- fires-perMinute: 5000
- memory:
- limit-min: 128m
- limit-std: 256m
- limit-max: 2048m
- concurrency:
- limit-min: 1
- limit-std: 1
- limit-max: 8
- loadbalancer:
- timeout-factor: 2
- blackbox-fraction: 10%
- controller:
- javaOpts: "-Xmx2048M"
- loggingLevel: "INFO"
- invoker:
- javaOpts: "-Xmx2048M"
- loggingLevel: "INFO"
- containerPool:
- userMemory: "8192m"
- redis:
- volume-size: 10
- default:
- password: s0meP@ass3
- nuvolaris:
- prefix: nuv
- password: s0meP@ass3
- mongodb:
- host: mongodb
- volume-size: 30
- admin:
- user: whisk_admin
- password: 0therPa55
- nuvolaris:
- user: nuvolaris
- password: s0meP@ass3
- exposedExternally: False
- useOperator: False
- minio:
- volume-size: 20
- admin:
- user: minioadmin
- password: minioadmin
- nuvolaris:
- user: nuvolaris
- password: zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG
- monitoring:
- prometheus:
- volume-size: 20
- alert-manager:
- enabled: true
- volume-size: 2
- slack:
- enabled: true
- default: true
- slack_channel_name: '#monitoring-nuvolaris'
- slack_api_url: ${SLACK_API_URL}
- gmail:
- enabled: true
- default: false
- from: ${ALERT_EMAIL_FROM}
- to: ${ALERT_EMAIL_TO}
- username: ${GMAIL_USERNAME}
- password: ${GMAIL_PASSWORD}
- postgres:
- volume-size: 30
- replicas: 2
- admin:
- password: 0therPa55
- replica-password: 0therPa55RR
- nuvolaris:
- password: s0meP@ass3
\ No newline at end of file
diff --git a/tests/k3s/whisk-full-faas.yaml b/tests/k3s/whisk-full-faas.yaml
deleted file mode 100644
index fd31fda..0000000
--- a/tests/k3s/whisk-full-faas.yaml
+++ /dev/null
@@ -1,172 +0,0 @@
-# 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.
-#
-apiVersion: nuvolaris.org/v1
-kind: Whisk
-metadata:
- name: controller
- namespace: nuvolaris
-spec:
- nuvolaris:
- password: nuvpassw0rd
- apihost: nuvolaris.org
- components:
- # start openwhisk controller
- openwhisk: true
- # start openwhisk invoker
- invoker: true
- # start couchdb
- couchdb: true
- # start kafka
- kafka: true
- # start mongodb
- mongodb: true
- # start redis
- redis: true
- # start cron based action parser
- cron: true
- # enable TLS
- tls: true
- # minio enabled or not
- minio: true
- # zookeeper enabled or not
- zookeeper: true
- # minio static enabled or not
- static: true
- # monitoring enabled or not
- monitoring: true
- # postgres enabled or not
- postgres: true
- openwhisk:
- namespaces:
- whisk-system: 789c46b1-71f6-4ed5-8c54-816aa4f8c502:abcfO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP
- nuvolaris: cbd68075-dac2-475e-8c07-d62a30c7e683:123zO3xKCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP
- couchdb:
- host: couchdb
- volume-size: 30
- admin:
- user: whisk_admin
- password: some_passw0rd
- controller:
- user: invoker_admin
- password: s0meP@ass1
- invoker:
- user: controller_admin
- password: s0meP@ass2
- kafka:
- host: kafka
- volume-size: 30
- controller:
- protocol: "http"
- host : "controller"
- port: "3233"
- image: "ghcr.io/nuvolaris/openwhisk-controller"
- tag: "0.3.0-morpheus.22122609"
- invoker:
- protocol: "http"
- host : "invoker"
- port: "8080"
- image: "ghcr.io/nuvolaris/openwhisk-invoker"
- tag: "0.3.0-morpheus.22122609"
- scheduler:
- schedule: "* * * * *"
- tls:
- acme-registered-email: francesco@nuvolaris.io
- acme-server-url: https://acme-v02.api.letsencrypt.org/directory
- zookeeper:
- host: zookeeper
- data-volume-size: 10
- log-volume-size: 5
- configs:
- limits:
- actions:
- sequence-maxLength: 500
- invokes-perMinute: 9999
- invokes-concurrent: 2500
- triggers:
- fires-perMinute: 5000
- memory:
- limit-min: 128m
- limit-std: 256m
- limit-max: 2048m
- concurrency:
- limit-min: 1
- limit-std: 1
- limit-max: 8
- loadbalancer:
- timeout-factor: 2
- blackbox-fraction: 10%
- controller:
- javaOpts: "-Xmx2048M"
- loggingLevel: "INFO"
- invoker:
- javaOpts: "-Xmx2048M"
- loggingLevel: "INFO"
- containerPool:
- userMemory: "8192m"
- redis:
- volume-size: 10
- default:
- password: s0meP@ass3
- nuvolaris:
- prefix: nuv
- password: s0meP@ass3
- mongodb:
- host: mongodb
- volume-size: 30
- admin:
- user: whisk_admin
- password: 0therPa55
- nuvolaris:
- user: nuvolaris
- password: s0meP@ass3
- exposedExternally: False
- useOperator: False
- minio:
- volume-size: 20
- admin:
- user: minioadmin
- password: minioadmin
- nuvolaris:
- user: nuvolaris
- password: zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG
- monitoring:
- prometheus:
- volume-size: 20
- alert-manager:
- enabled: true
- volume-size: 2
- slack:
- enabled: true
- default: true
- slack_channel_name: '#monitoring-nuvolaris'
- slack_api_url: ${SLACK_API_URL}
- gmail:
- enabled: true
- default: false
- from: ${ALERT_EMAIL_FROM}
- to: ${ALERT_EMAIL_TO}
- username: ${GMAIL_USERNAME}
- password: ${GMAIL_PASSWORD}
- postgres:
- volume-size: 30
- replicas: 2
- admin:
- password: 0therPa55
- replica-password: 0therPa55RR
- nuvolaris:
- password: s0meP@ass3
\ No newline at end of file
diff --git a/tests/k3s/whisk-full-mysc2.yaml b/tests/k3s/whisk-full-mysc2.yaml
deleted file mode 100644
index 143b8d9..0000000
--- a/tests/k3s/whisk-full-mysc2.yaml
+++ /dev/null
@@ -1,181 +0,0 @@
-# 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.
-#
-apiVersion: nuvolaris.org/v1
-kind: Whisk
-metadata:
- name: controller
- namespace: nuvolaris
-spec:
- nuvolaris:
- apihost: arcosicilia.org
- password: nuvpassw0rd
- protocol: https
- affinity: true
- tolerations: true
- components:
- # start openwhisk controller
- openwhisk: true
- # start openwhisk invoker
- invoker: true
- # start couchdb
- couchdb: true
- # start kafka
- kafka: true
- # start mongodb
- mongodb: true
- # start redis
- redis: true
- # start cron based action parser
- cron: true
- # enable TLS
- tls: false
- # minio enabled or not
- minio: true
- # zookeeper enabled or not
- zookeeper: true
- # minio static enabled or not
- static: true
- # monitoring enabled or not
- monitoring: true
- # postgres enabled or not
- postgres: true
- openwhisk:
- namespaces:
- whisk-system: 789c46b1-71f6-4ed5-8c54-816aa4f8c502:abcfO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP
- nuvolaris: cbd68075-dac2-475e-8c07-d62a30c7e683:123zO3xKCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP
- couchdb:
- host: couchdb
- volume-size: 10
- admin:
- user: whisk_admin
- password: some_passw0rd
- controller:
- user: invoker_admin
- password: s0meP@ass1
- invoker:
- user: controller_admin
- password: s0meP@ass2
- kafka:
- host: kafka
- volume-size: 10
- controller:
- protocol: "http"
- host : "controller"
- port: "3233"
- image: "ghcr.io/nuvolaris/openwhisk-controller"
- tag: "3.1.0-mastrogpt.2402101445"
- invoker:
- protocol: "http"
- host : "invoker"
- port: "8080"
- image: "ghcr.io/nuvolaris/openwhisk-invoker"
- tag: "3.1.0-mastrogpt.2402101445"
- scheduler:
- schedule: "* * * * *"
- tls:
- acme-registered-email: francesco@nuvolaris.io
- acme-server-url: https://acme-v02.api.letsencrypt.org/directory
- zookeeper:
- host: zookeeper
- data-volume-size: 10
- log-volume-size: 5
- configs:
- limits:
- actions:
- sequence-maxLength: 500
- invokes-perMinute: 9999
- invokes-concurrent: 2500
- triggers:
- fires-perMinute: 5000
- memory:
- limit-min: 128m
- limit-std: 256m
- limit-max: 2048m
- concurrency:
- limit-min: 1
- limit-std: 1
- limit-max: 1
- loadbalancer:
- timeout-factor: 2
- blackbox-fraction: 20%
- controller:
- javaOpts: "-Xmx4096M"
- loggingLevel: "INFO"
- replicas: 1
- invoker:
- javaOpts: "-Xmx2048M"
- loggingLevel: "INFO"
- replicas: 3
- containerPool:
- userMemory: "12288m"
- kubernetes:
- user_pod_affinity_enabled: true
- user_pod_affinity_key : "nuvolaris-role"
- user_pod_affinity_value : "invoker"
- redis:
- volume-size: 5
- default:
- password: s0meP@ass3
- nuvolaris:
- prefix: nuv
- password: s0meP@ass3
- mongodb:
- host: mongodb
- volume-size: 10
- admin:
- user: whisk_admin
- password: 0therPa55
- nuvolaris:
- user: nuvolaris
- password: s0meP@ass3
- exposedExternally: False
- useOperator: False
- minio:
- volume-size: 10
- admin:
- user: minioadmin
- password: minioadmin
- nuvolaris:
- user: nuvolaris
- password: zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG
- monitoring:
- prometheus:
- volume-size: 10
- alert-manager:
- enabled: true
- volume-size: 2
- slack:
- enabled: true
- default: true
- slack_channel_name: '#monitoring-nuvolaris'
- slack_api_url: ${SLACK_API_URL}
- gmail:
- enabled: true
- default: false
- from: ${ALERT_EMAIL_FROM}
- to: ${ALERT_EMAIL_TO}
- username: ${GMAIL_USERNAME}
- password: ${GMAIL_PASSWORD}
- postgres:
- volume-size: 5
- replicas: 2
- admin:
- password: 0therPa55
- replica-password: 0therPa55RR
- nuvolaris:
- password: s0meP@ass3
\ No newline at end of file
diff --git a/tests/k3s/whisk-full-saas.yaml b/tests/k3s/whisk-full-saas.yaml
deleted file mode 100644
index 3064559..0000000
--- a/tests/k3s/whisk-full-saas.yaml
+++ /dev/null
@@ -1,174 +0,0 @@
-# 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.
-#
-apiVersion: nuvolaris.org/v1
-kind: Whisk
-metadata:
- name: controller
- namespace: nuvolaris
-spec:
- nuvolaris:
- password: nuvpassw0rd
- apihost: 146.148.119.191
- storageclass: rook-ceph-block
- provisioner: rook-ceph.rbd.csi.ceph.com
- components:
- # start openwhisk controller
- openwhisk: true
- # start openwhisk invoker
- invoker: true
- # start couchdb
- couchdb: true
- # start kafka
- kafka: true
- # start mongodb
- mongodb: true
- # start redis
- redis: true
- # start cron based action parser
- cron: true
- # enable TLS
- tls: false
- # minio enabled or not
- minio: true
- # zookeeper enabled or not
- zookeeper: true
- # minio static enabled or not
- static: true
- # monitoring enabled or not
- monitoring: true
- # postgres enabled or not
- postgres: true
- openwhisk:
- namespaces:
- whisk-system: 789c46b1-71f6-4ed5-8c54-816aa4f8c502:abcfO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP
- nuvolaris: cbd68075-dac2-475e-8c07-d62a30c7e683:123zO3xKCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP
- couchdb:
- host: couchdb
- volume-size: 10
- admin:
- user: whisk_admin
- password: some_passw0rd
- controller:
- user: invoker_admin
- password: s0meP@ass1
- invoker:
- user: controller_admin
- password: s0meP@ass2
- kafka:
- host: kafka
- volume-size: 10
- controller:
- protocol: "http"
- host : "controller"
- port: "3233"
- image: "ghcr.io/nuvolaris/openwhisk-controller"
- tag: "0.3.0-morpheus.22122609"
- invoker:
- protocol: "http"
- host : "invoker"
- port: "8080"
- image: "ghcr.io/nuvolaris/openwhisk-invoker"
- tag: "0.3.0-morpheus.22122609"
- scheduler:
- schedule: "* * * * *"
- tls:
- acme-registered-email: francesco@nuvolaris.io
- acme-server-url: https://acme-v02.api.letsencrypt.org/directory
- zookeeper:
- host: zookeeper
- data-volume-size: 10
- log-volume-size: 5
- configs:
- limits:
- actions:
- sequence-maxLength: 500
- invokes-perMinute: 9999
- invokes-concurrent: 2500
- triggers:
- fires-perMinute: 5000
- memory:
- limit-min: 128m
- limit-std: 256m
- limit-max: 2048m
- concurrency:
- limit-min: 1
- limit-std: 1
- limit-max: 1
- loadbalancer:
- timeout-factor: 2
- blackbox-fraction: 25%
- controller:
- javaOpts: "-Xmx2048M"
- loggingLevel: "INFO"
- invoker:
- javaOpts: "-Xmx8192M"
- loggingLevel: "INFO"
- containerPool:
- userMemory: "51200m"
- redis:
- volume-size: 5
- default:
- password: s0meP@ass3
- nuvolaris:
- prefix: nuv
- password: s0meP@ass3
- mongodb:
- host: mongodb
- volume-size: 10
- admin:
- user: whisk_admin
- password: 0therPa55
- nuvolaris:
- user: nuvolaris
- password: s0meP@ass3
- exposedExternally: False
- useOperator: False
- minio:
- volume-size: 10
- admin:
- user: minioadmin
- password: minioadmin
- nuvolaris:
- user: nuvolaris
- password: zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG
- monitoring:
- prometheus:
- volume-size: 10
- alert-manager:
- enabled: true
- volume-size: 2
- slack:
- enabled: true
- default: true
- slack_channel_name: '#monitoring-nuvolaris'
- slack_api_url: ${SLACK_API_URL}
- gmail:
- enabled: true
- default: false
- from: ${ALERT_EMAIL_FROM}
- to: ${ALERT_EMAIL_TO}
- username: ${GMAIL_USERNAME}
- password: ${GMAIL_PASSWORD}
- postgres:
- volume-size: 5
- replicas: 2
- admin:
- password: 0therPa55
- replica-password: 0therPa55RR
- nuvolaris:
- password: s0meP@ass3
\ No newline at end of file
diff --git a/tests/k3s/whisk-full.yaml b/tests/k3s/whisk-full.yaml
deleted file mode 100644
index c6c39e6..0000000
--- a/tests/k3s/whisk-full.yaml
+++ /dev/null
@@ -1,174 +0,0 @@
-# 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.
-#
-apiVersion: nuvolaris.org/v1
-kind: Whisk
-metadata:
- name: controller
- namespace: nuvolaris
-spec:
- nuvolaris:
- apihost: api.nuvolaris.org
- password: nuvpassw0rd
- #apiport: 80
- #protocol: http
- components:
- # start openwhisk controller
- openwhisk: true
- # start openwhisk invoker
- invoker: true
- # start couchdb
- couchdb: true
- # start kafka
- kafka: true
- # start mongodb
- mongodb: true
- # start redis
- redis: true
- # start cron based action parser
- cron: true
- # enable TLS
- tls: true
- # minio enabled or not
- minio: true
- # zookeeper enabled or not
- zookeeper: true
- # minio static enabled or not
- static: true
- # monitoring enabled or not
- monitoring: true
- # postgres enabled or not
- postgres: true
- openwhisk:
- namespaces:
- whisk-system: 789c46b1-71f6-4ed5-8c54-816aa4f8c502:abcfO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP
- nuvolaris: cbd68075-dac2-475e-8c07-d62a30c7e683:123zO3xKCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP
- couchdb:
- host: couchdb
- volume-size: 10
- admin:
- user: whisk_admin
- password: some_passw0rd
- controller:
- user: invoker_admin
- password: s0meP@ass1
- invoker:
- user: controller_admin
- password: s0meP@ass2
- kafka:
- host: kafka
- volume-size: 10
- controller:
- protocol: "http"
- host : "controller"
- port: "3233"
- image: "ghcr.io/nuvolaris/openwhisk-controller"
- tag: "0.3.0-morpheus.22122609"
- invoker:
- protocol: "http"
- host : "invoker"
- port: "8080"
- image: "ghcr.io/nuvolaris/openwhisk-invoker"
- tag: "0.3.0-morpheus.22122609"
- scheduler:
- schedule: "* * * * *"
- tls:
- acme-registered-email: francesco@nuvolaris.io
- acme-server-url: https://acme-v02.api.letsencrypt.org/directory
- zookeeper:
- host: zookeeper
- data-volume-size: 10
- log-volume-size: 5
- configs:
- limits:
- actions:
- sequence-maxLength: 500
- invokes-perMinute: 9999
- invokes-concurrent: 2500
- triggers:
- fires-perMinute: 5000
- memory:
- limit-min: 128m
- limit-std: 256m
- limit-max: 2048m
- concurrency:
- limit-min: 1
- limit-std: 1
- limit-max: 1
- loadbalancer:
- timeout-factor: 2
- blackbox-fraction: 25%
- controller:
- javaOpts: "-Xmx2048M"
- loggingLevel: "INFO"
- invoker:
- javaOpts: "-Xmx8192M"
- loggingLevel: "INFO"
- containerPool:
- userMemory: "51200m"
- redis:
- volume-size: 5
- default:
- password: s0meP@ass3
- nuvolaris:
- prefix: nuv
- password: s0meP@ass3
- mongodb:
- host: mongodb
- volume-size: 10
- admin:
- user: whisk_admin
- password: 0therPa55
- nuvolaris:
- user: nuvolaris
- password: s0meP@ass3
- exposedExternally: False
- useOperator: False
- minio:
- volume-size: 10
- admin:
- user: minioadmin
- password: minioadmin
- nuvolaris:
- user: nuvolaris
- password: zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG
- monitoring:
- prometheus:
- volume-size: 10
- alert-manager:
- enabled: true
- volume-size: 2
- slack:
- enabled: true
- default: true
- slack_channel_name: '#monitoring-nuvolaris'
- slack_api_url: ${SLACK_API_URL}
- gmail:
- enabled: true
- default: false
- from: ${ALERT_EMAIL_FROM}
- to: ${ALERT_EMAIL_TO}
- username: ${GMAIL_USERNAME}
- password: ${GMAIL_PASSWORD}
- postgres:
- volume-size: 5
- replicas: 2
- admin:
- password: 0therPa55
- replica-password: 0therPa55RR
- nuvolaris:
- password: s0meP@ass3
\ No newline at end of file
diff --git a/tests/k3s/whisk.yaml b/tests/k3s/whisk.yaml
index ed6f656..7894733 100644
--- a/tests/k3s/whisk.yaml
+++ b/tests/k3s/whisk.yaml
@@ -22,38 +22,41 @@ metadata:
namespace: nuvolaris
spec:
nuvolaris:
- password: nuvpassw0rd
- apihost: auto #api.k3s.nuvtest.net
+ apihost: localhost
+ password: nuvpassw0rd
components:
# start openwhisk controller
openwhisk: true
# start openwhisk invoker
- invoker: false
+ invoker: true
# start couchdb
couchdb: true
# start kafka
- kafka: false
+ kafka: true
# start mongodb
mongodb: true
# start redis
redis: true
# start cron based action parser
- cron: true
+ cron: true
# enable TLS
- tls: true
+ tls: false
# minio enabled or not
- minio: true
+ minio: true
+ # zookeeper enabled or not
+ zookeeper: true
# minio static enabled or not
- static: true
+ static: true
+ # monitoring enabled or not
+ monitoring: true
# postgres enabled or not
- postgres: true
+ postgres: true
openwhisk:
namespaces:
whisk-system: 789c46b1-71f6-4ed5-8c54-816aa4f8c502:abcfO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP
- nuvolaris: cbd68075-dac2-475e-8c07-d62a30c7e683:123zO3xKCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP
+ nuvolaris: cbd68075-dac2-475e-8c07-d62a30c7e683:123zO3xKCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP
couchdb:
host: couchdb
- port: 5984
volume-size: 10
admin:
user: whisk_admin
@@ -67,24 +70,56 @@ spec:
kafka:
host: kafka
volume-size: 10
+ controller:
+ protocol: "http"
+ host : "controller"
+ port: "3233"
+ image: "ghcr.io/nuvolaris/openwhisk-controller"
+ tag: "0.3.0-morpheus.22122609"
+ invoker:
+ protocol: "http"
+ host : "invoker"
+ port: "8080"
+ image: "ghcr.io/nuvolaris/openwhisk-invoker"
+ tag: "0.3.0-morpheus.22122609"
scheduler:
schedule: "* * * * *"
tls:
acme-registered-email: francesco@nuvolaris.io
acme-server-url: https://acme-v02.api.letsencrypt.org/directory
- configs:
+ zookeeper:
+ host: zookeeper
+ data-volume-size: 10
+ log-volume-size: 5
+ configs:
limits:
actions:
- sequence-maxLength: 50
- invokes-perMinute: 999
- invokes-concurrent: 250
+ sequence-maxLength: 500
+ invokes-perMinute: 9999
+ invokes-concurrent: 2500
triggers:
- fires-perMinute: 999
+ fires-perMinute: 5000
+ memory:
+ limit-min: 128m
+ limit-std: 256m
+ limit-max: 2048m
+ concurrency:
+ limit-min: 1
+ limit-std: 1
+ limit-max: 1
+ loadbalancer:
+ timeout-factor: 2
+ blackbox-fraction: 25%
controller:
javaOpts: "-Xmx2048M"
+ loggingLevel: "INFO"
+ invoker:
+ javaOpts: "-Xmx8192M"
+ loggingLevel: "INFO"
+ containerPool:
+ userMemory: "51200m"
redis:
- persistence-enabled: true
- volume-size: 10
+ volume-size: 5
default:
password: s0meP@ass3
nuvolaris:
@@ -102,13 +137,31 @@ spec:
exposedExternally: False
useOperator: False
minio:
- volume-size: 2
+ volume-size: 10
admin:
user: minioadmin
password: minioadmin
nuvolaris:
user: nuvolaris
- password: zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG
+ password: zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG
+ monitoring:
+ prometheus:
+ volume-size: 10
+ alert-manager:
+ enabled: true
+ volume-size: 2
+ slack:
+ enabled: true
+ default: true
+ slack_channel_name: '#monitoring-openserverless'
+ slack_api_url: ${SLACK_API_URL}
+ gmail:
+ enabled: true
+ default: false
+ from: ${ALERT_EMAIL_FROM}
+ to: ${ALERT_EMAIL_TO}
+ username: ${GMAIL_USERNAME}
+ password: ${GMAIL_PASSWORD}
postgres:
volume-size: 5
replicas: 2
@@ -116,5 +169,4 @@ spec:
password: 0therPa55
replica-password: 0therPa55RR
nuvolaris:
- password: s0meP@ass3
-
+ password: s0meP@ass3
\ No newline at end of file
diff --git a/tests/microk8s/monitoring-ingress.yaml b/tests/microk8s/monitoring-ingress.yaml
index 5d67ed6..0e56726 100644
--- a/tests/microk8s/monitoring-ingress.yaml
+++ b/tests/microk8s/monitoring-ingress.yaml
@@ -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.
+#
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
(openserverless-operator) 04/24: chore: updates to taskfile
Posted by fr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
franztt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openserverless-operator.git
commit c8080d4a83b43dc785f39c12699133bc9c7aee3a
Author: francescotimperi <fr...@gmail.com>
AuthorDate: Sat Jul 13 07:41:05 2024 +0000
chore: updates to taskfile
---
.env.dist | 1 +
Dockerfile | 2 +-
Taskfile.yml | 4 ++++
3 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/.env.dist b/.env.dist
new file mode 100644
index 0000000..072d08a
--- /dev/null
+++ b/.env.dist
@@ -0,0 +1 @@
+GITHUB_USER=github: put here your github user
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index 395ba23..887d551 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -21,7 +21,7 @@ ENV CONTROLLER_TAG=3.1.0-mastrogpt.2402101445
ENV INVOKER_IMAGE=ghcr.io/nuvolaris/openwhisk-invoker
ENV INVOKER_TAG=3.1.0-mastrogpt.2402101445
ARG OPERATOR_IMAGE_DEFAULT=ghcr.io/nuvolaris/openserverless-operator
-ARG OPERATOR_TAG_DEFAULT=1.3.0-beta.2309191654
+ARG OPERATOR_TAG_DEFAULT=0.1-dev.2309191654
ENV OPERATOR_IMAGE=${OPERATOR_IMAGE_DEFAULT}
ENV OPERATOR_TAG=${OPERATOR_TAG_DEFAULT}
diff --git a/Taskfile.yml b/Taskfile.yml
index f138fbf..89ddda4 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -336,9 +336,13 @@ tasks:
env:
TAG: "{{.TAG}}"
cmds:
+ - kubectl delete pod/kaniko
+ - kubectl delete cm/dockerfile-configmap
- test -n "$GITHUB_USER" || true "did you configure your .env?"
- envsubst <kaniko.yaml >_kaniko.yaml
- cat _kaniko.yaml
+ - kubectl -n default create configmap dockerfile-configmap --from-file=Dockerfile
+ - kubectl apply -f _kaniko.yaml
shell:
- kubectl -n nuvolaris exec --stdin --tty {{.POD}} -- /bin/bash
(openserverless-operator) 06/24: feat: adds mc and wsk CLI via nix
Posted by fr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
franztt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openserverless-operator.git
commit 6f602d730906521f6a268437d207fbffbadb11e4
Author: francescotimperi <fr...@gmail.com>
AuthorDate: Sat Jul 13 15:21:18 2024 +0000
feat: adds mc and wsk CLI via nix
---
shell.nix | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 57 insertions(+), 1 deletion(-)
diff --git a/shell.nix b/shell.nix
index 3d4487b..eb1d670 100644
--- a/shell.nix
+++ b/shell.nix
@@ -19,6 +19,61 @@
let
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-23.11";
pkgs = import nixpkgs { config = {}; overlays = []; };
+
+ #
+ # Align the version of minio-client to the same used by openserverless operator if changed
+ # and update also the sha256 value accordingly.
+ #
+ minio-client = pkgs.stdenv.mkDerivation rec {
+ pname = "minio-client";
+ version = "RELEASE.2023-03-23T20-03-04Z";
+ src = pkgs.fetchurl {
+ url = "https://dl.min.io/client/mc/release/linux-amd64/archive/mc.${version}";
+ sha256 = "5de4aec1fc6c6608723a1d6da988350c876d5eb14538000ccb4d3a226199ab89";
+ };
+ phases = [ "unpackPhase" "installPhase" ];
+ unpackPhase = ''
+ runHook preUnpack
+ mkdir -p $out
+ cp $src $out/mc.${version}
+ runHook postUnpack
+ '';
+ installPhase = ''
+ mkdir -p $out/bin
+ install -m755 $out/mc.${version} $out/bin/mc
+ '';
+ };
+
+ #
+ # Openwhisk wsk CLI it is still required
+ #
+ wsk = pkgs.stdenv.mkDerivation rec {
+ pname = "wsk";
+ version = "1.2.0";
+
+ src = pkgs.fetchurl {
+ url = "https://github.com/apache/openwhisk-cli/releases/download/${version}/OpenWhisk_CLI-${version}-linux-amd64.tgz";
+ sha256 = "c404ac124b7f98c49d293c9abae28740283ce2615248690e7652a94648780820";
+ };
+
+ phases = [ "unpackPhase" "installPhase" ];
+
+ unpackPhase = ''
+ runHook preUnpack
+
+ mkdir -p $out/tmp
+ cat $src| tar xzvf - -C $out/tmp
+
+ runHook postUnpack
+ '';
+
+ installPhase = ''
+ mkdir -p $out/bin
+ install -m755 $out/tmp/wsk $out/bin/wsk
+ '';
+
+ };
+
in
pkgs.mkShellNoCC {
@@ -28,7 +83,8 @@ pkgs.mkShellNoCC {
poetry
zip
unzip
- # minio-client-2023-03-23T20-03-04Z
+ minio-client
+ wsk
];
}
(openserverless-operator) 08/24: fix: removed erroneus status update in operator update and resume handlers
Posted by fr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
franztt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openserverless-operator.git
commit d8b325c668945b1d9ccc0bf35c8c479c1d067679
Author: francescotimperi <fr...@gmail.com>
AuthorDate: Tue Jul 16 19:25:43 2024 +0000
fix: removed erroneus status update in operator update and resume handlers
---
nuvolaris/main.py | 2 --
1 file changed, 2 deletions(-)
diff --git a/nuvolaris/main.py b/nuvolaris/main.py
index 19f74df..fd9c44f 100644
--- a/nuvolaris/main.py
+++ b/nuvolaris/main.py
@@ -339,13 +339,11 @@ def whisk_update(spec, status, namespace, diff, name, **kwargs):
owner = kube.get(f"wsk/{name}")
patcher.patch(diff, status, owner, name)
- status['controller']= "Updated"
@kopf.on.resume('nuvolaris.org', 'v1', 'whisks')
def whisk_resume(spec, status, name, **kwargs):
operator_util.config_from_spec(spec, handler_type="on_resume")
operator_util.whisk_post_resume(name)
- status['controller']='Resumed'
def runtimes_filter(name, type, **kwargs):
return name == 'openwhisk-runtimes' and type == 'MODIFIED'
(openserverless-operator) 10/24: Merge pull request #8 from francescotimperi/handler-fix
Posted by fr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
franztt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openserverless-operator.git
commit 8c21a285e6b277f0d8f0bfcb87568ba2552fa070
Merge: 9fc799b 4d8ee0c
Author: Francesco Timperi Tiberi <fr...@gmail.com>
AuthorDate: Thu Jul 18 10:59:47 2024 +0200
Merge pull request #8 from francescotimperi/handler-fix
fix: resume and update handlers status patch fails
nuvolaris/main.py | 2 --
nuvolaris/quota_checker.py | 2 +-
2 files changed, 1 insertion(+), 3 deletions(-)
(openserverless-operator) 15/24: Merge pull request #12 from francescotimperi/main
Posted by fr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
franztt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openserverless-operator.git
commit ad9b05d07ef52e712dbe8413e3a974527c81cdde
Merge: 11e176d a705f51
Author: Francesco Timperi Tiberi <fr...@gmail.com>
AuthorDate: Tue Jul 23 17:55:19 2024 +0200
Merge pull request #12 from francescotimperi/main
Implements apache/openserverless#22
DISCLAIMER | 11 +++++++++++
NOTICE | 6 ++++++
Taskfile.yml | 2 +-
3 files changed, 18 insertions(+), 1 deletion(-)
(openserverless-operator) 01/24: first commit
Posted by fr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
franztt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openserverless-operator.git
commit d17d6564855fc3884d68caff7d4f007f24c14105
Author: Francesco Timperi Tiberi <fr...@gmail.com>
AuthorDate: Tue Jul 9 17:19:33 2024 +0000
first commit
---
DESIGN.md | 67 +
Dockerfile | 127 +
LICENSE | 218 +
OPERATOR.md | 247 +
README.md | 204 +
Taskfile.yml | 345 ++
TaskfileBuild.yml | 63 +
TaskfileDev.yml | 104 +
TaskfileEnterprise.yml | 71 +
TaskfileNuvEnterprise.yml | 91 +
TaskfileOlaris.yml | 29 +
TaskfileTest.yml | 197 +
actions/Taskfile.yml | 448 ++
actions/common/authorize.py | 162 +
actions/common/command_data.py | 50 +
actions/common/minio_util.py | 182 +
actions/common/util.py | 35 +
actions/content/__main__.py | 110 +
actions/content/common/minio_util.py | 182 +
actions/devel/download/__main__.py | 91 +
actions/devel/download/common/authorize.py | 162 +
actions/devel/download/common/command_data.py | 50 +
actions/devel/download/common/minio_util.py | 182 +
actions/devel/download/common/util.py | 35 +
actions/devel/download/flatdict.py | 506 ++
actions/devel/download/nuvolaris/config.py | 202 +
actions/devel/download/nuvolaris/couchdb_util.py | 164 +
actions/devel/ferretdb/__main__.py | 73 +
actions/devel/ferretdb/command/ferretbd.py | 75 +
actions/devel/ferretdb/common/authorize.py | 162 +
actions/devel/ferretdb/common/command_data.py | 50 +
actions/devel/ferretdb/common/minio_util.py | 182 +
actions/devel/ferretdb/common/util.py | 35 +
actions/devel/ferretdb/flatdict.py | 506 ++
actions/devel/ferretdb/nuvolaris/config.py | 202 +
actions/devel/ferretdb/nuvolaris/couchdb_util.py | 164 +
actions/devel/minio/__main__.py | 77 +
actions/devel/minio/command/minio.py | 170 +
actions/devel/minio/common/authorize.py | 162 +
actions/devel/minio/common/command_data.py | 50 +
actions/devel/minio/common/minio_util.py | 182 +
actions/devel/minio/common/util.py | 35 +
actions/devel/minio/flatdict.py | 506 ++
actions/devel/minio/nuvolaris/config.py | 202 +
actions/devel/minio/nuvolaris/couchdb_util.py | 164 +
actions/devel/psql/__main__.py | 74 +
actions/devel/psql/command/psql.py | 84 +
actions/devel/psql/common/authorize.py | 162 +
actions/devel/psql/common/command_data.py | 50 +
actions/devel/psql/common/minio_util.py | 182 +
actions/devel/psql/common/util.py | 35 +
actions/devel/psql/flatdict.py | 506 ++
actions/devel/psql/nuvolaris/config.py | 202 +
actions/devel/psql/nuvolaris/couchdb_util.py | 164 +
actions/devel/redis/__main__.py | 72 +
actions/devel/redis/command/redis.py | 55 +
actions/devel/redis/common/authorize.py | 162 +
actions/devel/redis/common/command_data.py | 50 +
actions/devel/redis/common/minio_util.py | 182 +
actions/devel/redis/common/util.py | 35 +
actions/devel/redis/flatdict.py | 506 ++
actions/devel/redis/nuvolaris/config.py | 202 +
actions/devel/redis/nuvolaris/couchdb_util.py | 164 +
actions/devel/upload/__main__.py | 115 +
actions/devel/upload/common/authorize.py | 162 +
actions/devel/upload/common/command_data.py | 50 +
actions/devel/upload/common/minio_util.py | 182 +
actions/devel/upload/common/util.py | 35 +
actions/devel/upload/flatdict.py | 506 ++
actions/devel/upload/nuvolaris/config.py | 202 +
actions/devel/upload/nuvolaris/couchdb_util.py | 164 +
actions/github/_playground.txt | 744 +++
actions/github/callback/__main__.py | 98 +
actions/github/common/config_exception.py | 19 +
actions/github/common/github_client.py | 96 +
actions/github/common/html_response_generator.py | 76 +
actions/github/common/kube_api_client.py | 143 +
actions/github/common/whisk_user_generator.py | 71 +
actions/github/webhook/__main__.py | 113 +
actions/login/__main__.py | 96 +
actions/login/flatdict.py | 506 ++
actions/system.yml | 126 +
clusters/aks.yml | 32 +
clusters/eks-cluster.yml | 40 +
clusters/eks.yml | 48 +
clusters/gke.yml | 40 +
clusters/juju-aws-overlay.yaml | 29 +
clusters/juju.yml | 25 +
clusters/k3s.yml | 40 +
clusters/kind.yml | 40 +
clusters/lks.yml | 51 +
clusters/microk8s.cf | 86 +
clusters/microk8s.yml | 95 +
clusters/openshift-aggregate.yaml | 41 +
clusters/openshift-install-config.yaml | 55 +
clusters/openshift-user.yaml | 40 +
clusters/openshift.yml | 63 +
clusters/talos-gcp.yml | 107 +
cron.sh | 19 +
dbinit.sh | 19 +
demos/devel/mdb_find.json | 1 +
demos/devel/mdb_insert.json | 18 +
demos/devel/minio_buckets.json | 3 +
demos/devel/minio_cp.json | 9 +
demos/devel/minio_ls.json | 6 +
demos/devel/minio_mv.json | 9 +
demos/devel/minio_rm.json | 7 +
demos/devel/psql_create1.json | 3 +
demos/devel/psql_create2.json | 3 +
demos/devel/psql_create3.json | 3 +
demos/devel/psql_desc.json | 3 +
demos/devel/psql_select.json | 3 +
demos/devel/redis_get.json | 3 +
demos/devel/redis_set.json | 3 +
demos/hellonode/.gitignore | 19 +
demos/hellonode/Dockerfile | 26 +
demos/hellonode/app.js | 30 +
demos/hellonode/deploy.yaml | 55 +
demos/hellonode/package-lock.json | 1018 ++++
demos/hellonode/package.json | 15 +
demos/operator/demo-crd.yaml | 71 +
demos/operator/demo-deployment.yaml | 37 +
demos/operator/demo-ns.yaml | 22 +
demos/operator/demo-obj.yaml | 26 +
demos/operator/demo1.py | 39 +
demos/operator/demo2.py | 61 +
demos/operator/demomock.py | 27 +
demos/operator/obj.yaml | 32 +
demos/operator/patch.yaml | 24 +
demos/operator/poetry.lock | 1179 +++++
demos/operator/pyproject.toml | 35 +
demos/operator/transcript.txt | 78 +
demos/velero/Taskfile.yml | 154 +
demos/velero/values.yaml | 538 ++
deploy/Taskfile.yml | 64 +
deploy/_deprecated/preload-runtime-go117.yaml | 29 +
deploy/_deprecated/preload-runtime-nodej14.yaml | 28 +
deploy/_deprecated/preload-runtime-python37.yaml | 29 +
deploy/_deprecated/standalone-kcf.yaml | 77 +
deploy/_deprecated/standalone-lb.yaml | 29 +
deploy/_deprecated/standalone-np.yaml | 58 +
deploy/_deprecated/standalone-set.yaml | 61 +
deploy/_deprecated/standalone-test-in-https.yaml | 47 +
deploy/acme-openshift/acme-rbac.yaml | 114 +
deploy/acme-openshift/acme-setup.yaml | 58 +
deploy/alert-manager/alertmanager-02-cm.yaml | 32 +
deploy/alert-manager/alertmanager-03-dep.yaml | 100 +
deploy/alert-manager/alertmanager-04-svc.yaml | 37 +
deploy/apisix-etcd/etcd-policy.yaml | 31 +
deploy/apisix-etcd/etcd-pvc.yaml | 32 +
deploy/apisix-etcd/etcd-secret.yaml | 28 +
deploy/apisix-etcd/etcd-sts.yaml | 160 +
deploy/apisix-etcd/etcd-svc.yaml | 64 +
deploy/apisix/apisix-cm.yaml | 248 +
deploy/apisix/apisix-dep.yaml | 83 +
deploy/apisix/apisix-svc.yaml | 60 +
deploy/cert-manager/cert-manager.yaml | 5521 ++++++++++++++++++++
deploy/content/index.html | 32 +
deploy/couchdb/couchdb-svc.yaml | 45 +
deploy/ferretdb/ferretdb-sts.yaml | 44 +
deploy/ferretdb/ferretdb-svc.yaml | 31 +
deploy/ingress-nginx/cloud-nginx-ingress.yaml | 666 +++
deploy/ingress-nginx/eks-nginx-ingress.yaml | 680 +++
deploy/ingress-nginx/kind-nginx-ingress.yaml | 690 +++
deploy/issuer/cluster-issuer.yaml | 36 +
deploy/kafka/kafka-000-pvc.yaml | 33 +
deploy/kafka/kafka-001-sts.yaml | 107 +
deploy/kafka/kafka-003-svc.yaml | 38 +
deploy/kvrocks/kvrocks-pvc.yaml | 28 +
deploy/kvrocks/kvrocks-sts.yaml | 62 +
deploy/kvrocks/kvrocks-svc.yaml | 31 +
deploy/minio/00-minio-pvc.yaml | 32 +
deploy/minio/01-minio-dep.yaml | 62 +
deploy/minio/02-minio-svc.yaml | 37 +
.../mongodb-auth-nuvolaris.yaml | 25 +
deploy/mongodb-operator-deploy/mongodb-auth.yaml | 25 +
deploy/mongodb-operator-deploy/mongodb-svc.yaml | 34 +
deploy/mongodb-operator-deploy/mongodb.yaml | 52 +
deploy/mongodb-operator/mongodb-crd.yaml | 451 ++
deploy/mongodb-operator/mongodb-operator.yaml | 92 +
deploy/mongodb-operator/mongodb-rbac.yaml | 123 +
deploy/mongodb-standalone/mongodb-auth.yaml | 25 +
deploy/mongodb-standalone/mongodb-cm.yaml | 34 +
deploy/mongodb-standalone/mongodb-pvc.yaml | 28 +
deploy/mongodb-standalone/mongodb-sts.yaml | 87 +
deploy/mongodb-standalone/mongodb-svc.yaml | 31 +
deploy/monitoring/kubestate-03-dep.yaml | 114 +
deploy/monitoring/kubestate-04-svc.yaml | 38 +
deploy/monitoring/prometheus-02-cm.yaml | 906 ++++
deploy/monitoring/prometheus-03-dep.yaml | 101 +
deploy/monitoring/prometheus-04-svc.yaml | 38 +
deploy/nginx-static/nginx-static-cm.yaml | 111 +
deploy/nginx-static/nginx-static-service.yaml | 33 +
deploy/nginx-static/nginx-static-sts.yaml | 66 +
deploy/ngrok | 0
deploy/nuvolaris-operator/operator-pod.yaml | 38 +
deploy/nuvolaris-operator/whisk.yaml | 79 +
deploy/nuvolaris-permissions/kubegres-crd.yaml | 3757 +++++++++++++
deploy/nuvolaris-permissions/nuvolaris-common.yaml | 36 +
.../nuvolaris-kubegres-common.yaml | 23 +
.../nuvolaris-kubegres-roles.yaml | 252 +
.../nuvolaris-monitoring-00-sa.yaml | 46 +
.../nuvolaris-monitoring-01-roles.yaml | 295 ++
.../nuvolaris-quota-common.yaml | 25 +
.../nuvolaris-quota-roles.yaml | 52 +
.../nuvolaris-wsku-common.yaml | 25 +
.../nuvolaris-wsku-roles.yaml | 57 +
.../openwhisk-core-roles.yaml | 57 +
.../nuvolaris-permissions/openwhisk-core-sa.yaml | 37 +
.../openwhisk-invoker-sa.yaml | 61 +
.../openwhisk-runtimes-cm.yaml | 401 ++
.../operator-clusterroles.yaml | 112 +
deploy/nuvolaris-permissions/operator-roles.yaml | 114 +
deploy/nuvolaris-permissions/whisk-crd.yaml | 827 +++
deploy/nuvolaris-permissions/whisk-user-crd.yaml | 230 +
deploy/nuvolaris-permissions/workflows-crd.yaml | 79 +
.../openwhisk-endpoint/operator-ingress-rbac.yaml | 52 +
deploy/openwhisk-enterprise/enterprise-np.yaml | 30 +
deploy/openwhisk-enterprise/enterprise-sts.yaml | 211 +
deploy/openwhisk-invoker/invoker-sts.yaml | 277 +
deploy/openwhisk-invoker/invoker-svc.yaml | 29 +
deploy/openwhisk-standalone/standalone-ip.yaml | 29 +
deploy/openwhisk-standalone/standalone-np.yaml | 44 +
deploy/openwhisk-standalone/standalone-sts.yaml | 240 +
deploy/postgres-backup/postgres-backup-conf.yaml | 60 +
deploy/postgres-backup/postgres-backup-sts.yaml | 90 +
deploy/postgres-operator-deploy/postgres-auth.yaml | 27 +
deploy/postgres-operator-deploy/postgres-conf.yaml | 60 +
deploy/postgres-operator-deploy/postgres.yaml | 45 +
deploy/postgres-operator/kubegres-operator.yaml | 142 +
deploy/qdrant/qdrant-cm.yaml | 47 +
deploy/qdrant/qdrant-sts.yaml | 141 +
deploy/qdrant/qdrant-svc.yaml | 80 +
deploy/quota/quota_checker.yaml | 53 +
deploy/redis/_redis-cm.yaml | 28 +
deploy/redis/redis-set.yaml | 56 +
deploy/redis/redis-svc.yaml | 31 +
deploy/runtimes/runtimes-preload.yaml | 46 +
deploy/s3ninja/s3ninja-set.yaml | 45 +
deploy/s3ninja/s3ninja-svc.yaml | 30 +
deploy/scheduler/cron.yaml | 43 +
deploy/test-cm/_index.html | 27 +
deploy/test-cm/pod.yaml | 35 +
deploy/test-cm/svc.yaml | 30 +
deploy/test-sts/nginx-set.yaml | 39 +
deploy/test-sts/nginx-svc.yaml | 30 +
deploy/test/_crd.yaml | 54 +
deploy/test/_obj.yaml | 25 +
deploy/test/_pv.yaml | 31 +
deploy/test/_pvc.yaml | 25 +
deploy/test/_test.json | 4 +
deploy/test/_testcm.yaml | 23 +
deploy/test/pod.yaml | 27 +
deploy/test/svc.yaml | 26 +
deploy/whisk-system/.gitkeep | 0
deploy/zookeeper/zookeeper-000-cm.yaml | 67 +
deploy/zookeeper/zookeeper-001-pvc.yaml | 47 +
deploy/zookeeper/zookeeper-003-sts.yaml | 87 +
deploy/zookeeper/zookeeper-004-svc.yaml | 35 +
detect.sh | 48 +
enterprise-util/couchdb/couchdb-nuvolaris-app.yaml | 42 +
.../couchdb/couchdb-nuvolaris-org-ingress.yaml | 42 +
enterprise-util/faas/.git-keep | 0
enterprise-util/faas/openwhisk-runtimes-cm.yaml | 289 +
enterprise-util/faas/whisk_nuvorg.yaml | 152 +
enterprise-util/kafka/Dockerfile | 22 +
enterprise-util/kafka/README.md | 27 +
enterprise-util/kafka/Taskfile.yml | 36 +
.../kafka/jmx/jmx_prometheus_javaagent-0.18.0.jar | Bin 0 -> 544974 bytes
.../kafka/jmx/prometheus-jmx-agent-config.yml | 141 +
enterprise-util/minio/minio-aks-ingress.yaml | 42 +
.../minio/minio-nuvolaris-org-ingress.yaml | 42 +
enterprise-util/minio/minio-saa1-ingress.yaml | 37 +
.../monitoring/prometheus-localhost.yaml | 37 +
.../prometheus-mistergpt-app-ingress.yaml | 37 +
.../prometheus-nuvolaris-app-ingress.yaml | 42 +
.../prometheus-nuvolaris-org-ingress.yaml | 42 +
enterprise/.git-keep | 0
init.src | 28 +
nuvolaris/__init__.py | 18 +
nuvolaris/actionexecutor.py | 396 ++
nuvolaris/apihost_util.py | 309 ++
nuvolaris/bucket.py | 31 +
nuvolaris/ceph_cos.py | 346 ++
nuvolaris/ceph_cos_ingress.py | 150 +
nuvolaris/ceph_cos_obc_data.py | 134 +
nuvolaris/ceph_cos_user_data.py | 137 +
nuvolaris/certmanager.py | 68 +
nuvolaris/config.py | 258 +
nuvolaris/couchdb.py | 277 +
nuvolaris/couchdb_util.py | 170 +
nuvolaris/cronjob.py | 99 +
nuvolaris/endpoint.py | 332 ++
nuvolaris/enterprise_util.py | 226 +
nuvolaris/ferretdb.py | 219 +
nuvolaris/files/README.md | 26 +
...vations_design_document_for_activations_db.json | 9 +
...uth_design_document_for_subjects_db_v2.0.0.json | 10 +
nuvolaris/files/filter_design_document.json | 6 +
...Cleanup_design_document_for_activations_db.json | 9 +
...hrottlings_design_document_for_subjects_db.json | 10 +
nuvolaris/files/runtimes-nodeonly.json | 46 +
nuvolaris/files/runtimes.json | 268 +
...document_for_activations_db_filters_v2.1.0.json | 10 +
...document_for_activations_db_filters_v2.1.1.json | 10 +
..._design_document_for_activations_db_v2.1.0.json | 10 +
...sks_design_document_for_entities_db_v2.1.0.json | 26 +
nuvolaris/ingress.py | 96 +
nuvolaris/ingress_data.py | 124 +
nuvolaris/invoker.py | 63 +
nuvolaris/ip_util.py | 108 +
nuvolaris/issuer.py | 99 +
nuvolaris/kafka.py | 81 +
nuvolaris/kopf_util.py | 171 +
nuvolaris/kube.py | 173 +
nuvolaris/kustomize.py | 349 ++
nuvolaris/kvrocks.py | 233 +
nuvolaris/main.py | 377 ++
nuvolaris/minio_deploy.py | 309 ++
nuvolaris/minio_ingress.py | 215 +
nuvolaris/minio_util.py | 169 +
nuvolaris/mongodb.py | 191 +
nuvolaris/mongodb_operator.py | 135 +
nuvolaris/mongodb_standalone.py | 73 +
nuvolaris/monitoring.py | 145 +
nuvolaris/nuvolaris_metadata.py | 87 +
nuvolaris/openwhisk.py | 43 +
nuvolaris/openwhisk_enterprise.py | 63 +
nuvolaris/openwhisk_standalone.py | 61 +
nuvolaris/operator_util.py | 135 +
nuvolaris/patcher.py | 177 +
nuvolaris/policies/minio_rw_policy_tpl.json | 19 +
nuvolaris/postgres_client.py | 145 +
nuvolaris/postgres_operator.py | 308 ++
nuvolaris/quota_checker.py | 215 +
nuvolaris/quota_checker_job.py | 105 +
nuvolaris/redis.py | 242 +
nuvolaris/redis_client.py | 130 +
nuvolaris/route_data.py | 87 +
nuvolaris/runtimes_preloader.py | 65 +
nuvolaris/runtimes_util.py | 55 +
nuvolaris/s3_bucket_policy.py | 98 +
nuvolaris/s3_client.py | 200 +
nuvolaris/storage_static.py | 267 +
nuvolaris/template.py | 84 +
nuvolaris/templates/00-minio-pvc.yaml | 32 +
nuvolaris/templates/01-minio-dep.yaml | 73 +
nuvolaris/templates/02-minio-svc.yaml | 37 +
.../affinity-tolerance-dep-core-attach.yaml | 62 +
.../affinity-tolerance-sts-core-attach.yaml | 62 +
.../affinity-tolerance-sts-invoker-attach.yaml | 54 +
nuvolaris/templates/alert-manager-02-cm.yaml | 113 +
nuvolaris/templates/cluster-issuer.yaml | 40 +
nuvolaris/templates/content.html | 27 +
nuvolaris/templates/cosi-obc-tpl.yaml | 29 +
nuvolaris/templates/cosi-user-tpl.yaml | 33 +
nuvolaris/templates/couchdb-init.yaml | 45 +
nuvolaris/templates/couchdb-set-tpl.yaml | 84 +
nuvolaris/templates/cron-init.yaml | 41 +
nuvolaris/templates/cronjob-attach.yaml | 31 +
nuvolaris/templates/dbname_pgpass_tpl.properties | 18 +
nuvolaris/templates/dep-attach.yaml | 47 +
nuvolaris/templates/enterprise-np.yaml | 30 +
nuvolaris/templates/enterprise-sts.yaml | 237 +
nuvolaris/templates/ferretdb-sts.yaml | 49 +
nuvolaris/templates/generic-ingress-tpl.yaml | 71 +
.../templates/generic-openshift-route-tpl.yaml | 45 +
nuvolaris/templates/index.html | 27 +
nuvolaris/templates/ingress.yaml | 32 +
nuvolaris/templates/invoker-sts.yaml | 308 ++
nuvolaris/templates/invoker-svc.yaml | 29 +
nuvolaris/templates/kafka-000-pvc.yaml | 33 +
nuvolaris/templates/kafka-001-sts.yaml | 109 +
nuvolaris/templates/kvrocks-cm.yaml | 936 ++++
nuvolaris/templates/kvrocks_manage_user_tpl.txt | 8 +
nuvolaris/templates/mongodb-auth-nuvolaris.yaml | 25 +
nuvolaris/templates/mongodb-auth.yaml | 25 +
nuvolaris/templates/mongodb-cm.yaml | 34 +
nuvolaris/templates/mongodb-config.yaml | 77 +
nuvolaris/templates/mongodb-sts.yaml | 90 +
nuvolaris/templates/mongodb_manage_user_tpl.js | 37 +
nuvolaris/templates/nginx-static-cm.yaml | 115 +
nuvolaris/templates/nginx-static-sts.yaml | 66 +
nuvolaris/templates/pgpass_tpl.properties | 18 +
nuvolaris/templates/postgres-backup-sts.yaml | 90 +
nuvolaris/templates/postgres.yaml | 76 +
.../postgres_manage_user_extension_tpl.sql | 30 +
nuvolaris/templates/postgres_manage_user_tpl.sql | 36 +
nuvolaris/templates/pv-create.yaml | 32 +
nuvolaris/templates/pvc-attach.yaml | 28 +
nuvolaris/templates/pvc-create.yaml | 31 +
nuvolaris/templates/quota-cronjob-attach.yaml | 34 +
nuvolaris/templates/redis-conf.yaml | 57 +
nuvolaris/templates/redis-set.yaml | 61 +
nuvolaris/templates/redis_manage_user_tpl.txt | 33 +
nuvolaris/templates/redis_quota_checker.lua | 21 +
nuvolaris/templates/redis_quota_checker.sh | 5 +
nuvolaris/templates/resources-sts-attach.yaml | 33 +
.../templates/runtimes-job-container-attach.yaml | 35 +
nuvolaris/templates/security-dep-attach.yaml | 36 +
nuvolaris/templates/security-global-attach.yaml | 28 +
nuvolaris/templates/security-set-attach.yaml | 36 +
nuvolaris/templates/set-attach.yaml | 43 +
nuvolaris/templates/standalone-sts.yaml | 258 +
nuvolaris/templates/subject.json | 11 +
nuvolaris/templates/test.json | 4 +
nuvolaris/templates/testcm.yaml | 23 +
nuvolaris/templates/traefik-middleware-tpl.yaml | 33 +
nuvolaris/templates/user_metadata.json | 28 +
nuvolaris/templates/whisk-system-manifest-tpl.yaml | 31 +
nuvolaris/templates/workflow-job.yaml | 42 +
nuvolaris/templates/zookeeper-001-pvc.yaml | 29 +
nuvolaris/templates/zookeeper-002-pvc.yaml | 29 +
nuvolaris/testutil.py | 303 ++
nuvolaris/time_util.py | 54 +
nuvolaris/user_config.py | 81 +
nuvolaris/user_handlers.py | 181 +
nuvolaris/user_metadata.py | 66 +
nuvolaris/user_patcher.py | 46 +
nuvolaris/userdb_util.py | 128 +
nuvolaris/util.py | 656 +++
nuvolaris/whisk_actions_deployer.py | 206 +
nuvolaris/whisk_system_util.py | 44 +
nuvolaris/workflows.py | 96 +
nuvolaris/zookeeper.py | 76 +
poetry.lock | 1678 ++++++
profile.ipy | 40 +
pyproject.toml | 49 +
quota.sh | 19 +
run.sh | 33 +
test_profile.ipy | 20 +
tests/aks/whisk.yaml | 120 +
tests/api.js | 32 +
tests/config_test.ipy | 62 +
tests/couchdb_test.ipy | 34 +
tests/cron_test.ipy | 25 +
tests/echo.js | 27 +
tests/eks/config_test.ipy | 23 +
tests/eks/whisk-full.yaml | 169 +
tests/eks/whisk-user.yaml | 44 +
tests/eks/whisk.yaml | 120 +
tests/generic/whisk-full.yaml | 153 +
tests/generic/whisk.yaml | 119 +
tests/gke/whisk.yaml | 120 +
tests/hello.js | 22 +
tests/hello.py | 24 +
tests/hi.py | 20 +
tests/invoker_test.ipy | 52 +
tests/k3s/config_test.ipy | 23 +
tests/k3s/ingress_test.ipy | 46 +
tests/k3s/minimal.yaml | 93 +
tests/k3s/traefik-middleware.yaml | 25 +
tests/k3s/whisk-demo-user.yaml | 47 +
tests/k3s/whisk-full-arm.yaml | 172 +
tests/k3s/whisk-full-faas.yaml | 172 +
tests/k3s/whisk-full-mysc2.yaml | 181 +
tests/k3s/whisk-full-saas.yaml | 174 +
tests/k3s/whisk-full.yaml | 174 +
tests/k3s/whisk-mysc2.yaml | 123 +
tests/k3s/whisk-saas.yaml | 174 +
tests/k3s/whisk-user.yaml | 47 +
tests/k3s/whisk.yaml | 120 +
tests/kafka_test.ipy | 54 +
tests/kind/action_executor_test.ipy | 48 +
tests/kind/config_test.ipy | 39 +
tests/kind/config_user_test.ipy | 24 +
tests/kind/couchdb_compactions_test.ipy | 55 +
tests/kind/couchdb_lib_test.ipy | 71 +
tests/kind/couchdb_util_test.ipy | 77 +
tests/kind/dbinit.ipy | 32 +
tests/kind/dbonly.yaml | 69 +
tests/kind/ferretdb_test.ipy | 42 +
tests/kind/minimal.yaml | 107 +
tests/kind/minio_static_test.ipy | 40 +
tests/kind/minio_test.ipy | 48 +
tests/kind/minio_util_test.ipy | 47 +
tests/kind/mongodb_op_test.ipy | 42 +
tests/kind/mongodb_std_test.ipy | 43 +
tests/kind/mongodb_user_test.ipy | 51 +
tests/kind/monitoring_test.ipy | 36 +
tests/kind/nuvolaris_subject_test.ipy | 70 +
tests/kind/openwhisk_enterprise_test.ipy | 89 +
tests/kind/postgres_op_test.ipy | 37 +
tests/kind/runtime_preloader_test.ipy | 35 +
tests/kind/userdb_util_test.ipy | 73 +
tests/kind/whisk-affinity.yaml | 137 +
tests/kind/whisk-full-affinity.yaml | 196 +
tests/kind/whisk-full-mf.yaml | 191 +
tests/kind/whisk-full-storage.yaml | 201 +
tests/kind/whisk-full.yaml | 206 +
tests/kind/whisk-gpt-user.yaml | 47 +
tests/kind/whisk-mf-demo.yaml | 140 +
tests/kind/whisk-minimal.yaml | 133 +
tests/kind/whisk-user.yaml | 51 +
tests/kind/whisk-user2.yaml | 51 +
tests/kind/whisk.yaml | 138 +
tests/kind/wskadmin.ipy | 43 +
tests/kube_test.py | 19 +
tests/kube_test.txt | 69 +
tests/kvrocks_test.ipy | 37 +
tests/kvrocks_user_test.ipy | 54 +
tests/lks/config_test.ipy | 24 +
tests/lks/whisk-full.yaml | 150 +
tests/lks/whisk.yaml | 118 +
tests/main_test.ipy | 36 +
tests/microk8s/config_test.ipy | 23 +
tests/microk8s/ingress_test.ipy | 46 +
tests/microk8s/monitoring-ingress.yaml | 24 +
tests/microk8s/whisk-full.yaml | 166 +
tests/microk8s/whisk-user.yaml | 47 +
tests/microk8s/whisk.yaml | 120 +
tests/minio-nuv.js | 47 +
tests/minio.js | 45 +
tests/mongo.js | 36 +
tests/mongo.yaml | 28 +
tests/mongodb_add_user_script.js | 30 +
tests/openshift/minimal.yaml | 103 +
tests/openshift/whisk-full.yaml | 138 +
tests/openshift/whisk-user.yaml | 47 +
tests/openshift/whisk.yaml | 119 +
tests/openwhisk_test.py | 64 +
tests/openwhisk_test.txt | 46 +
tests/patcher/openwhisk-runtimes-cm.yaml | 274 +
tests/patcher/whisk-full-baupanlabs.yaml | 134 +
tests/patcher/whisk-full-update.yaml | 137 +
tests/patcher/whisk-update.yaml | 102 +
tests/ping.js | 29 +
tests/postgres.js | 54 +
tests/quota_checker_test.ipy | 33 +
tests/redis.js | 51 +
tests/redis_test.ipy | 37 +
tests/redis_user_test.ipy | 55 +
tests/runtimes.json | 251 +
tests/runtimes/openwhisk-runtimes-cm.yaml | 317 ++
tests/volume_test.ipy | 71 +
tests/whisk-full.yaml | 204 +
tests/whisk-user.yaml | 43 +
tests/whisk.yaml | 119 +
tests/workflow-test.yaml | 36 +
tests/workflows_test.ipy | 55 +
tests/zookeeper_test.ipy | 43 +
tools/cli/wsk/wskadmin.py | 744 +++
tools/cli/wsk/wskprop.py | 82 +
tools/cli/wsk/wskutil.py | 106 +
whisk-system.sh | 83 +
wskadmin.sh | 48 +
546 files changed, 68246 insertions(+)
diff --git a/DESIGN.md b/DESIGN.md
new file mode 100644
index 0000000..423b5fc
--- /dev/null
+++ b/DESIGN.md
@@ -0,0 +1,67 @@
+<!--
+ ~ 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.
+ ~
+-->
+# Design Document
+
+The operator is built with `kopf`. It is basically a server that will react to events coming from Kubernetes. The main evento to react to is the deployment of a Custom Resource Definition that configures OpenWhisk and all the related components.
+
+All the code is in the package `nuvolaris`. Events are handled in `main.py`.
+
+Each event handlers uses utility functions you can find the various service specific files like `openwhisk` or `couchdb`.
+
+## Development Workflow
+
+In generale the operator react to events and execute kustomizations.
+To develop,
+- you start and test writing a base YAML configuration in code.
+- you can then modify this base configuration in code using `kustomize` (wrapped in python)
+- finally you apply the configuration with `kubectl` (wapped in python)
+
+A good example of how to build a configuration is the following
+
+You start creating a basic definition under `deploy` for example `couchdb`. All the utils searches set of deployments under some folder under `deploy`.
+
+Then you can do this (obvious details omitted)
+
+```
+1 kust = kus.secretLiteral("couchdb-auth", user, pasw)
+2 kust += kus.patchTemplate("couchdb", "set-attach.yaml", data)
+3 spec = kus.kustom_list("couchdb", kust, templates=["couchdb-init.yaml"], data=data)
+4 kube.apply(spec)
+
+```
+
+In `1` you create a customization to create a secret.
+
+In `2` there is a customization to create a `patch` for it. There are a few utility functions for that in the `nuvolaris.kustomize` package. Some of them are actually templatized (have Template name) so the actual file is generated expanding a template. All the templates are under `nuvolaris/templates`. A templatized configuration uses `Jinja2`. When you use templates you also have to provide the `data` dictionary for the templates.
+
+3. Once you finisced you generate a configuration to be applied. Usually it is better to be a series of configuration, hence you invoke `kustom_list`. Note that kustom list, in addition to customize a configuration, can also add more configuration in the form of templates. Specificy the templates to use as `templates=templates` and do not forget to provide `data=data` to provide data
+
+4. Finally you apply the configuration with `kube.apply` - there are more utility functions running `kubectl` in a pythonic way.
+
+# Testing
+
+There are multiple level of testings
+
+- you first test algorithm with unit tests (`task utest` - I use a lot doctest)
+- you then test with integration tests without starting the operator (`task itest`) - tests uses ipython and assertions
+- you can run the operator locally without deploying it (with `task run`) and run task `deploy` to deploy a test CRD.
+- you can then deploy in local kind with `task build-and-load` without having to publish it, then execute tests (see the TaskfileTest.yml)
+- finally you test built the image and publish it with `task image-tag ; git push --tags`, wait it builds and test against real kubernetes, using the Taskfle
+
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..395ba23
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,127 @@
+# 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.
+#
+FROM ubuntu:22.04
+ENV CONTROLLER_IMAGE=ghcr.io/nuvolaris/openwhisk-controller
+ENV CONTROLLER_TAG=3.1.0-mastrogpt.2402101445
+ENV INVOKER_IMAGE=ghcr.io/nuvolaris/openwhisk-invoker
+ENV INVOKER_TAG=3.1.0-mastrogpt.2402101445
+ARG OPERATOR_IMAGE_DEFAULT=ghcr.io/nuvolaris/openserverless-operator
+ARG OPERATOR_TAG_DEFAULT=1.3.0-beta.2309191654
+ENV OPERATOR_IMAGE=${OPERATOR_IMAGE_DEFAULT}
+ENV OPERATOR_TAG=${OPERATOR_TAG_DEFAULT}
+
+# configure dpkg && timezone
+RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
+ENV TZ=Europe/London
+RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
+# install Python
+RUN apt-get update && apt-get -y upgrade &&\
+ apt-get -y install apt-utils python3.10 python3.10-venv curl sudo telnet inetutils-ping zip unzip
+# Download Kubectl
+RUN KVER="v1.23.0" ;\
+ ARCH="$(dpkg --print-architecture)" ;\
+ KURL="https://dl.k8s.io/release/$KVER/bin/linux/$ARCH/kubectl" ;\
+ curl -sL $KURL -o /usr/bin/kubectl && chmod +x /usr/bin/kubectl
+RUN VER="v4.5.7" ;\
+ ARCH="$(dpkg --print-architecture)" ;\
+ URL="https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2F$VER/kustomize_${VER}_linux_${ARCH}.tar.gz" ;\
+ curl -sL "$URL" | tar xzvf - -C /usr/bin
+# Download WSK
+RUN WSK_VERSION=1.2.0 ;\
+ WSK_BASE=https://github.com/apache/openwhisk-cli/releases/download ;\
+ ARCH=$(dpkg --print-architecture) ;\
+ WSK_URL="$WSK_BASE/$WSK_VERSION/OpenWhisk_CLI-$WSK_VERSION-linux-$ARCH.tgz" ;\
+ curl -sL "$WSK_URL" | tar xzvf - -C /usr/bin/
+# Download MINIO client
+RUN rm -Rvf /tmp/minio-binaries ;\
+ mkdir /tmp/minio-binaries ;\
+ MINIO_BASE=https://dl.min.io/client/mc/release/linux ;\
+ ARCH=$(dpkg --print-architecture) ;\
+ MC_VER=RELEASE.2023-03-23T20-03-04Z ;\
+ MINIO_URL="$MINIO_BASE-$ARCH/archive/mc.${MC_VER}" ;\
+ curl -sL "$MINIO_URL" --create-dirs -o /tmp/minio-binaries/mc ;\
+ chmod +x /tmp/minio-binaries/mc ;\
+ mv /tmp/minio-binaries/mc /usr/bin/mc ;\
+ rm -Rvf /tmp/minio-binaries
+# Download and instal task
+RUN sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/bin
+# add nuvolaris user
+RUN groupadd --gid 1001 nuvolaris
+RUN useradd -m nuvolaris -s /bin/bash --uid 1001 --gid 1001 --groups root
+RUN echo "nuvolaris ALL=(ALL:ALL) NOPASSWD: ALL" >>/etc/sudoers
+
+#RUN useradd -m -s /bin/bash -g root -N nuvolaris && \
+# echo "nuvolaris ALL=(ALL:ALL) NOPASSWD: ALL" >>/etc/sudoers
+
+WORKDIR /home/nuvolaris
+# install the operator
+ADD nuvolaris/*.py /home/nuvolaris/nuvolaris/
+ADD nuvolaris/files /home/nuvolaris/nuvolaris/files
+ADD nuvolaris/templates /home/nuvolaris/nuvolaris/templates
+ADD nuvolaris/policies /home/nuvolaris/nuvolaris/policies
+ADD deploy/nuvolaris-operator /home/nuvolaris/deploy/nuvolaris-operator
+ADD deploy/nuvolaris-permissions /home/nuvolaris/deploy/nuvolaris-permissions
+ADD deploy/openwhisk-standalone /home/nuvolaris/deploy/openwhisk-standalone
+ADD deploy/openwhisk-endpoint /home/nuvolaris/deploy/openwhisk-endpoint
+ADD deploy/couchdb /home/nuvolaris/deploy/couchdb
+ADD deploy/redis /home/nuvolaris/deploy/redis
+ADD deploy/scheduler /home/nuvolaris/deploy/scheduler
+ADD deploy/mongodb-operator /home/nuvolaris/deploy/mongodb-operator
+ADD deploy/mongodb-operator-deploy /home/nuvolaris/deploy/mongodb-operator-deploy
+ADD deploy/mongodb-standalone /home/nuvolaris/deploy/mongodb-standalone
+ADD deploy/cert-manager /home/nuvolaris/deploy/cert-manager
+ADD deploy/ingress-nginx /home/nuvolaris/deploy/ingress-nginx
+ADD deploy/issuer /home/nuvolaris/deploy/issuer
+ADD deploy/minio /home/nuvolaris/deploy/minio
+ADD deploy/kafka /home/nuvolaris/deploy/kafka
+ADD deploy/zookeeper /home/nuvolaris/deploy/zookeeper
+ADD deploy/nginx-static /home/nuvolaris/deploy/nginx-static
+ADD deploy/content /home/nuvolaris/deploy/content
+ADD deploy/postgres-operator /home/nuvolaris/deploy/postgres-operator
+ADD deploy/postgres-operator-deploy /home/nuvolaris/deploy/postgres-operator-deploy
+ADD deploy/ferretdb /home/nuvolaris/deploy/ferretdb
+ADD deploy/runtimes /home/nuvolaris/deploy/runtimes
+ADD deploy/postgres-backup /home/nuvolaris/deploy/postgres-backup
+ADD run.sh dbinit.sh cron.sh pyproject.toml poetry.lock whisk-system.sh /home/nuvolaris/
+
+# prepares the required folders to deploy the whisk-system actions
+RUN mkdir /home/nuvolaris/deploy/whisk-system
+ADD actions /home/nuvolaris/actions
+
+# enterprise specific
+ADD deploy/openwhisk-enterprise /home/nuvolaris/deploy/openwhisk-enterprise
+ADD deploy/openwhisk-invoker /home/nuvolaris/deploy/openwhisk-invoker
+ADD deploy/monitoring /home/nuvolaris/deploy/monitoring
+ADD deploy/alert-manager /home/nuvolaris/deploy/alert-manager
+ADD deploy/quota /home/nuvolaris/deploy/quota
+ADD deploy/kvrocks /home/nuvolaris/deploy/kvrocks
+ADD quota.sh /home/nuvolaris/
+
+USER nuvolaris
+ENV PATH=/home/nuvolaris/.local/bin:/usr/local/bin:/usr/bin:/sbin:/bin
+RUN curl -sSL https://install.python-poetry.org | python3.10 -
+RUN poetry install
+USER root
+RUN chown -R nuvolaris /home/nuvolaris ;\
+ chgrp -R root /home/nuvolaris ;\
+ chmod -R 0775 /home/nuvolaris
+USER nuvolaris
+ENV HOME=/home/nuvolaris
+RUN ./whisk-system.sh
+RUN cd deploy && tar cvf ../deploy.tar *
+CMD ./run.sh
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..dc2c22e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,218 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed 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.
+
+
+========================================================================
+Apache License 2.0
+========================================================================
+
+This distribution bundles the following components, which are available under an Apache License 2.0 (https://opensource.org/licenses/Apache-2.0).
+Spray Caching 1.3.4 (io.spray:spray-caching_2.11:1.3.4 - http://spray.io/documentation/1.2.4/spray-caching/)
+ ConcurrentMapBackedCache.scala under common/scala/src/main/scala/org/apache/openwhisk/core/database/ contains implementation
+ from Spray's [[spray.caching.Cache]] and [[spray.caching.SimpleLruCache]] respectively.
+ License included at licenses/LICENSE-spray.txt, or https://github.com/spray/spray/blob/master/LICENSE
+ Copyright (C) 2011-2015 the spray project <http://spray.io>
+
+This product bundles the files gradlew and gradlew.bat from Gradle v5.5
+which are distributed under the Apache License, Version 2.0.
+For details see ./gradlew and ./gradlew.bat.
\ No newline at end of file
diff --git a/OPERATOR.md b/OPERATOR.md
new file mode 100644
index 0000000..6f41912
--- /dev/null
+++ b/OPERATOR.md
@@ -0,0 +1,247 @@
+<!--
+ ~ 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.
+ ~
+-->
+# Apache OpenServerless Operator
+
+This is the Enterprise Kubernetes Operator of the [bit.ly/nuvolaris](nuvolaris project).
+
+## Developer notes
+
+We try to keep the enterprise version as closed as possible with the standalone version from the [bit.ly/nuvolaris](nuvolaris project).
+
+These features are exclusively provided by this enterprise version:
+
+- Kafka deployment including the corresponding Zookeeper
+- Openwhisk Invoker fully integrated with Zookeeper and Kafka
+- Openwhisk Controller fully integrated with Kafka
+- Possibility to deploy Openshisk Controller in lean mode
+
+## Publishing the operator
+Once the operator is ready, you can build and test it a against a kubernetes cluster.
+First, generate a new image tag with `task image-tag`.
+You can test locally using the kind cluster (provided by default by the development environment) with `ent:build-and-load`.
+
+
+To test it against other clusters, you need to publish it to the nuvolaris private repository on docker.hub
+
+To do this, add to your `.env` the following variables:
+
+
+```
+MY_CONTROLLER_IMAGE=<your-user>/<your-name>
+DOCKER_HUB_USER=<docker.hub user>
+DOCKER_HUB_TOKEN=<docker hub token>
+```
+
+To override the tag of the operator you have to generate a git tag: `task image-tag`. Note the tag is unique for the current hour (it embeds: YYMMDDhh).
+
+If you set those variables you can use
+- `task ent:docker-login` to log to the current docker.hub registry
+- `task ent:build-and-push` to build for one single architecture (faster but limited to your architecture)
+- `task ent:buildx-and-push` to build for all the architectues (slower, used by the GitHub action)
+- `task ent:docker-hub-authorize` to upload to the current kubernetes runtime a secret to give the possibility to download the operator enterprise image
+
+## Customization notes
+
+Nuvolaris operators it is normally deploy using a `whisk.yaml` configuration file which is applied via the installer. Typically the customization file contains something like
+
+```
+apiVersion: nuvolaris.org/v1
+kind: Whisk
+metadata:
+ name: controller
+ namespace: nuvolaris
+spec:
+ nuvolaris:
+ apihost: <ip address or hostname to be assigned to nuvolaris controller ingress>
+ components:
+ # start openwhisk controller
+ openwhisk: true
+ # start couchdb
+ couchdb: true
+ # start mongodb
+ mongodb: true
+ # start redis
+ redis: true
+ # start simple internal cron
+ cron: true
+ # tls enabled or not
+ tls: false
+ # minio enabled or not
+ minio: true
+ # start kafka
+ kafka: true
+ # start openwhisk invoker
+ invoker: true
+ # zookeeper enabled or not
+ zookeeper: true
+ openwhisk:
+ namespaces:
+ whisk-system: xxxx:yyyyyy
+ nuvolaris: ccccc:zzzzz
+ couchdb:
+ host: couchdb
+ port: 5984
+ volume-size: 10
+ admin:
+ user: <couch_db_admin_user>
+ password: <couch_db_admin_pwd>
+ controller:
+ user: <couch_db_controller_user>
+ password: <couch_db_controller_user>
+ mongodb:
+ host: mongodb
+ volume-size: 10
+ admin:
+ user: <mongodb_db_admin_user>
+ password: <mongodb_db_admin_pwd>
+ nuvolaris:
+ user: <mongodb_db_nuvolaris_user>
+ password: <mongodb_db_nuvolaris_pwd>
+ useOperator: False
+ scheduler:
+ schedule: "* * * * *"
+ tls:
+ acme-registered-email: xxxxx@youremailserver.com
+ acme-server-url: https://acme-staging-v02.api.letsencrypt.org/directory
+ minio:
+ volume-size: 2
+ nuvolaris:
+ root-user: <minio_admin_user>
+ root-password: <minio_admin_pwd>
+ configs:
+ limits:
+ actions:
+ sequence-maxLength: 50
+ invokes-perMinute: 999
+ invokes-concurrent: 250
+ triggers:
+ fires-perMinute: 999
+ time:
+ limit-min: "100ms"
+ limit-std: "1min"
+ limit-max: "5min"
+ memory:
+ limit-min: "128m"
+ limit-std: "256m"
+ limit-max: "512m"
+ controller:
+ javaOpts: "-Xmx2048M"
+ invoker:
+ javaOpts: "-Xmx2048M"
+ containerPool:
+ userMemory: "4096m"
+```
+
+## Default configs values
+If the provided `whisk.yaml` does not specify any dynamic configuration parameteres under `configs` item, the Enterprise operators defaults to these values:
+
+- `configs.limit.actions.sequence-maxLength=50`
+- `configs.limit.actions.invokes-perMinute=60`
+- `configs.limit.actions.invokes-concurrent=30`
+- `configs.limit.actions.triggers.fires-perMinute=60`
+- `configs.limits.time.limit-min=100ms`
+- `configs.limits.time.limit-std=1min`
+- `configs.limits.time.limit-max=5min`
+- `configs.limits.memory.limit-min=128m`
+- `configs.limits.memory.limit-std=256m`
+- `configs.limits.memory.limit-max=512m`
+- `configs.controller.javaOpts=-Xmx1024M`
+- `configs.controller.loggingLevel=INFO`
+- `configs.invoker.javaOpts=-Xmx1024M`
+- `configs.invoker.loggingLevel=INFO`
+- `configs.invoker.containerPool.userMemory=2048m`
+
+## Openwhisk Enterprise hot deployment
+
+The enterprise operator supports hot deployment for both Openwhisk Controller & Invoker, i.e it is possible to modify some specific part of the configuration inside the `whisk.yaml` file and apply it again. The operator will automatically stop and redeploy controller & invoker to take into account the new settings.
+
+```
+ configs:
+ limits:
+ actions:
+ sequence-maxLength: 50
+ invokes-perMinute: 999
+ invokes-concurrent: 250
+ triggers:
+ fires-perMinute: 999
+ time:
+ limit-min: "100ms"
+ limit-std: "1min"
+ limit-max: "5min"
+ memory:
+ limit-min: "128m"
+ limit-std: "256m"
+ limit-max: "512m"
+ controller:
+ javaOpts: "-Xmx2048M"
+ loggingLevel: "INFO"
+ invoker:
+ javaOpts: "-Xmx2048M"
+ loggingLevel: "INFO"
+ containerPool:
+ userMemory: "4096m"
+```
+
+As an example, using the above configuration and taking into account the standard 256m memory size limit per action, gives the possibility to execute around 16 action concurrently [`As a general rule consider it as the result of controller.javaOpts/256m`]. To increase the number of estimated action that can be executed modify for example `controller.javaOpts:"-Xmx8192M"` and redeploy the modified `whisk.yaml`.
+
+To apply the new customization execute
+
+```
+kubectl -n nuvolaris apply -f whisk.yaml
+```
+
+## Deploying the controller in lean-mode
+
+The enterprise operator supports also the deployment of the sole controller in lean mode, by simply applying a `whisk.yaml` containing
+
+```
+apiVersion: nuvolaris.org/v1
+kind: Whisk
+metadata:
+ name: controller
+ namespace: nuvolaris
+spec:
+ nuvolaris:
+ apihost: <ip address or hostname to be assigned to nuvolaris controller ingress>
+ components:
+ # start openwhisk controller
+ openwhisk: true
+ # start couchdb
+ couchdb: true
+ # start mongodb
+ mongodb: true
+ # start redis
+ redis: true
+ # start simple internal cron
+ cron: true
+ # tls enabled or not
+ tls: false
+ # minio enabled or not
+ minio: true
+ # start kafka
+ kafka: false
+ # start openwhisk invoker
+ invoker: false
+ # zookeeper enabled or not
+ zookeeper: false
+...
+```
+
+In lean mode it is recommended to set at least `configs.controller.javaOpts=-Xmx2048M`
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..65f7e97
--- /dev/null
+++ b/README.md
@@ -0,0 +1,204 @@
+<!--
+ ~ 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.
+ ~
+-->
+# Apache OpenServerless Operator
+
+This is the Kubernetes Operator of the [bit.ly/nuvolaris](Apache OpenServerless project).
+
+If you are interested in developing it, please read the [design document](DESIGN.md). Please also read the [development environment document](https://github.com/nuvolaris/nuvolaris/blob/main/docs/DEVEL.md) to learn how to setup it.
+
+If you wonder what are the strange subfolders, here is an explanation:
+
+- `nuvolaris` are python modules, and implements the operator logic, coded in Python.
+
+So you generally build the `nuvolaris` operator as a docker image and publishi, build the `nuv` CLI and use the `olaris` tasks to install and manage it...
+
+## Developer notes
+
+The operator is built in Python with [kopf](https://kopf.readthedocs.io/en/stable/). You can find some [examples here](https://github.com/nolar/kopf/tree/main/examples).
+
+The operator uses under the hood [kustomize](https://kustomize.io/) and [kubectl](https://kubernetes.io/docs/reference/kubectl/) to interact with Kubernetes.
+
+Let's discuss how to:
+
+- Select the Kubernetes cluster to use
+- Develop interactively (cli and tests)
+- Run it without deploying
+- Test it in a local cluster without pushing an image to a registry
+- Push it to a public registry to use with external Kubernetes
+
+## Selecting the Kubernetes Clusters
+
+You have to run the operator against multiple clusters for test. So the operator has a provision to selection one to use and work with.
+
+You can use other clusters for testing and development. You should place all the configurations for the other clusters in the folder `clusters`, with extension `.kubeconfig`. See [below](#creating-a-new-cluster) for informations about this.
+
+If you use the development environment, using Docker and VSCode, you have available a Kind cluster ready for test. Use the task `task kind:config` to copy the configuration in the `clusters` folder if you plan to change cluster.
+
+In order to test the operator against different clusters, you can list all the cluster configurations available with `task` and then select one with `task N` where N is the number of the configuration in the list shown.
+
+Example:
+
+```
+$ task
+*** current: kind
+1 clusters/aks.kubeconfig
+2 clusters/eks.kubeconfig
+3 clusters/kind.kubeconfig
+4 clusters/microk8s.kubeconfig
+*** select with 'task #'
+$ task 2
+task: [2] task use N=2
+cluster: eks
+Kubeconfig user entry is using deprecated API version client.authentication.k8s.io/v1alpha1. Run 'aws eks update-kubeconfig' to update.
+NAME STATUS ROLES AGE VERSION
+ip-192-168-42-140.eu-central-1.compute.internal Ready <none> 49d v1.22.6-eks-7d68063
+```
+
+## Developing the operator interactively
+
+Start coding with `task cli`.
+
+You can then develop some code experimentally interacting with a python interpreter with the same libraries and some useful imports and configuration ready (most notably the autoreload).
+
+You can then execute some tests.
+
+- `task utest`: unit tests
+- `task itest`: integration tests
+
+This will run all the tests. You can select which test to run with `tast itest T=xxx` or `task utest T=xxx`. It will then execute all the tests whose file starts with `xxx`.
+
+## Developing the operator without deploying it
+
+You can then start the whole operator without having to deploy it. Start it with `task run`.
+
+Once you started the operarto pen another terminal and Use `task instance` to apply a crd instance depending on the current clusters.
+
+The crd instance used it is defined by the type of kubernetes custer you are using. The script './detect.sh' tries to detect it. The configuration used is under `tests/<detected>/whisk.yaml`
+
+If you have not changed the cluster the default cluster is 'kind' so it will apply the configuration `tests/kind/whisk.yaml`
+
+You can change the configuration passing the `WHISK` parameter (without the .yaml). So if you are running an `eks` cluster and you want to use your configuration `custom` the command `task instance WHISK=custom` will use `tests/eks/custom.yaml`
+
+Other useful tasks:
+
+- `task watch` starts watching deployed pods servics and nodes - useful to see what happens when you deploy
+- `task destroy` removes the instance but not the operator so you can recreate the cluster with `task instance` maybe with different parameters
+- `task clean` removes everything including the operator and pvc
+- If something get stuck and the cleanup does not complete `task defin` can help removing incomplete finalizers (you need to open another terminal to run it)
+
+## Testing the operator deploying it locally
+
+First and before all, you need a tag.
+
+Execute `task image-tag`. This command will delete all the other tag and generate a tag in the format `x.y.z-milestone.timestamp`. This tag is essential to identify your operator.
+
+When you are confident you can try the operator, in kind, use `task build-and-load`. This will build an instance and load it in the local kind. This step does not require you publish it to a public registry.
+
+You can then proceed deploying it with `task operator` to deploy it.
+
+You can then use `task instance` to deploy a crd instance, and finally `task actions` to test whisk running a few actions
+
+## Publishing the operator
+
+Once the operator is ready, you can build and test it a against a kubernetes cluster.
+
+First, generate a new image tag with `task image-tag`.
+
+You can test locally using the kind cluster (provided by default by the development environment) with `build-and-load`.
+
+To test it against other clusters, you need to publish it to a public repository.
+
+If you have push access to Nuvolaris repository just push the tag and it will trigger the build using the tag to tag the repository and it will be published in the nuvolaris package registry on GitHub.
+
+You can also publish an image to your own github repository for testing purposes. To do this, add to your `.env` the following variables:
+
+
+```
+MY_CONTROLLER_IMAGE=<your-user>/<your-name>
+GITHUB_USER=<your-user>
+GITHUB_TOKEN=<your-access-token>
+```
+
+You cannot override the tag of the operator you have to generate a git tag: `task image-tag`. Note the tag is unique for the current hour (it embeds: YYMMDDhh).
+
+If you set those variables you can use
+- `task b:docker-login` to log to the current docker registry
+- `task b:build-and-push` to build for one single architecture (faster but limited to your architecture)
+- `task buildx-and-push` to build for all the architectues (slower, used by the GitHub action)
+
+Remember that for GitHub Containter Registry you have to make public the image you are using to tlet it to be accessible by other Kubernetes
+
+### Running tests
+
+Once you have choosen the Kubernetes cluster and published the images on a public registry you can test it with `task dtest`, that will run a complete deployment in the current Kubernetes cluster.
+
+If you want to run all the tests (u, i, d) aginst the current cluster use `task test`
+
+Finally if you want to run the tests against all the configured kubernetes use `task all-kubes -- <target>`.
+
+This targes runs a group of tests against all the available clusters, so you can run all the tests against all the kubes with `task all-kubes -- test`
+
+# Reference
+
+- `use` (default task) lists all the clusters;
+- `1`, `2`...`9` selects the corresponding entry
+- `operator`: deploys the operator in the current cluster.
+- `instance`: deploy an instance of the configuration to build an actual cluster.
+- `destroy`: destroy the current deployment
+- `clean`: remove everything - if you get stuck use `defin`
+- `defin`: remove finalizer for the controller, useful if `clean` does not complete
+- `config`: once it is deployed, extracts the current configuration to use `wsk`
+- `actions`: deploy and run test actions
+- `utest` runs unit tests, filter with `T=<xxx>`
+- `itest` runs integration tests, filter with `T=<xxx>`
+- `iclean`: remote temp files generated by the tests
+- `dtest` deployment test
+- `hello`: runs a simple hello world test
+- `ping`: runs a ping test of redis
+- `all-kubes -- <target>`: execute the `<target>` against all the configured kubes
+
+Build targets
+
+- `b:buildx-and-push`: build and push the operator in multiple architectectures (slow)
+- `b:build-and-load`: build and load the operator in the kind cluster
+- `b:docker-login`: login into the github docker registry
+- `b:build-and-push`: build and push the operator in the current architecture
+
+Currently the following clusters, with respective suffix are supported (note not all the clusters are fully implemented, and you may need to edit and set manually variables - see the Taskfiles for reference).
+
+- Kubernetes Kind: `kind`
+- Amazon EKS: `eks`
+- Azure AKS: `aks`
+- Google GKE: `gke`
+- Ubuntu MicroK8s: `m8s`
+- Rancher K3S: `k3s`
+- RedHat OpenShift: `osh`
+
+See below for creating clusters and configurations. All the configuration for the available clusters are expected to be in `clusters/*.kubeconfig`
+
+Available commands for the cluster `xxx` (not always and not for all the cases):
+
+- `xxx:list`: list existing clusters
+- `xxx:create`: create a test cluster
+- `xxx:destroy`: destroy a test cluster
+- `xxx:config`: set the kubeconfig to the current cluster
+
+Note that `kind` is available in the development environment by defaut. You may want to use `task kind:config` to extract the configuration to be able to switch back to the local clusters.
+
diff --git a/Taskfile.yml b/Taskfile.yml
new file mode 100644
index 0000000..e6b17ef
--- /dev/null
+++ b/Taskfile.yml
@@ -0,0 +1,345 @@
+# 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.
+#
+version: '3'
+
+vars:
+ G: ""
+ N: ""
+ T: ""
+ P: ""
+ BASETAG: 1.3.0-mastrogpt
+ KUBE:
+ sh: ./detect.sh
+ NS: "nuvolaris"
+ PREFIX: ""
+
+dotenv:
+ - .env
+
+includes:
+ d:
+ taskfile: TaskfileDev.yml
+ t:
+ taskfile: TaskfileTest.yml
+ b:
+ taskfile: TaskfileBuild.yml
+ o:
+ taskfile: TaskfileOlaris.yml
+ kind:
+ taskfile: clusters/kind.yml
+ dir: clusters
+ eks:
+ taskfile: clusters/eks.yml
+ dir: clusters
+ aks:
+ taskfile: clusters/aks.yml
+ dir: clusters
+ lks:
+ taskfile: clusters/lks.yml
+ dir: clusters
+ gke:
+ taskfile: clusters/gke.yml
+ dir: clusters
+ mik:
+ taskfile: clusters/microk8s.yml
+ dir: clusters
+ ent:
+ taskfile: TaskfileEnterprise.yml
+ k3s:
+ taskfile: clusters/k3s.yml
+ dir: clusters
+ osh:
+ taskfile: clusters/openshift.yml
+ dir: clusters
+ sys:
+ taskfile: actions/system.yml
+ dir: actions
+ talos:
+ taskfile: clusters/talos-gcp.yml
+ dir: clusters
+ nuvent:
+ taskfile: TaskfileNuvEnterprise.yml
+
+tasks:
+
+ default:
+ - task: use
+
+ setup:
+ deps:
+ - update-files-from-openwhisk
+ - ssh-key
+ - configure-env
+ cmds:
+ - mkdir -p ~/.kube ; touch ~/.kube/config
+ - poetry install
+ status:
+ - test -e $GOBIN/kopf
+
+ env: env
+
+ watch: watch kubectl -n {{.NS}} get nodes,pod,svc,pvc,ingress
+ watch-osh: watch kubectl -n {{.NS}} get nodes,pod,svc,pvc,route
+ watch-cert: watch kubectl -n {{.NS}} get ingress,ClusterIssuers,Certificates,CertificateRequests,Orders,Challenges
+ watch-pod: watch kubectl -n {{.NS}} get po,job --no-headers
+
+ logs: >
+ rm -f nuvolaris-operator.log ;
+ kubectl -n nuvolaris logs pod/nuvolaris-operator -f | tee nuvolaris-operator.log
+
+ cli:
+ - task: d:cli
+
+ run:
+ - task: sys:prepare
+ - task: t:permission
+ - task: d:run
+
+ irun:
+ - task: instance
+ - task: d:run
+
+ permission:
+ - task: t:permission
+
+ operator:
+ - task: t:operator
+
+ instance:
+ - task: t:instance
+
+ instance-wfx:
+ - task: d:instance-wfx
+
+ instance-and-log:
+ - task: d:instance
+ - kubectl -n nuvolaris logs pod/nuvolaris-operator -f
+
+ mongo:
+ - task: t:mongo
+
+ minio:
+ - task: t:minio
+
+ postgres:
+ - task: t:postgres
+
+ minimal:
+ #- task: permission
+ - task: operator
+ - task instance WHISK=minimal
+ - task: config
+ - task: hello
+
+ all:
+ - task: permission
+ - task: operator
+ - task: instance
+ - task: config
+ - task: hello
+ - task: redis
+ - task: mongo
+ - task: minio
+ - task: postgres
+
+ config:
+ - task: t:config
+
+ hello:
+ - task: t:hello
+
+ redis:
+ - task: t:redis
+
+ workflow:
+ - kubectl -n nuvolaris apply -f tests/workflow-test.yaml
+
+ defin:
+ - task: d:defin
+
+ image-tag:
+ - git tag -d $(git tag)
+ - git tag -f {{.P}}{{.BASETAG}}.$(date +%y%m%d%H%M)
+ - env PAGER= git tag
+
+ # configure env
+ configure-env:
+ cmds:
+ - |
+ if ! test -e .env
+ then echo "please copy .env.dist in .env and add the keys"
+ fi
+
+ # update configuration files from openwhisk source
+ update-files-from-openwhisk:
+ cmds:
+ - |
+ cp -v ../nuvolaris/nuvolaris-controller/openwhisk/ansible/files/*.json nuvolaris/files
+ cp -v ../nuvolaris/nuvolaris-controller/openwhisk/bin/wskadmin tools/cli/wsk/wskadmin.py
+ cp -v ../nuvolaris/nuvolaris-controller/openwhisk/tools/admin/*.py tools/cli/wsk
+ sources:
+ - ../nuvolaris/nuvolaris-controller/openwhisk/ansible/files/*.json
+ - ../nuvolaris/nuvolaris-controller/openwhisk/bin/wskadmin
+ - ../nuvolaris/nuvolaris-controller/openwhisk/tools/admin/*.py
+ generates:
+ - nuvolaris/files/*.json
+ - nuvolaris/tools/cli/wsk/*.py
+
+ # generate ssh keys
+ ssh-key:
+ cmds:
+ - test -f clusters/id_rsa || ssh-keygen -b 2048 -t rsa -f clusters/id_rsa -q -N ""
+ - ssh-keygen -y -f clusters/id_rsa >clusters/id_rsa.pub
+
+ clean:
+ cmds:
+ - cmd: kubectl -n nuvolaris delete wsku --all
+ ignore_error: true
+ - cmd: kubectl -n nuvolaris delete wsk/controller --grace-period=0 --timeout=5s
+ ignore_error: true
+ - cmd: task defin
+ ignore_error: true
+ - cmd: kubectl -n nuvolaris delete all --all --grace-period=0
+ ignore_error: true
+ - cmd: kubectl -n nuvolaris delete pvc --all --grace-period=0
+ ignore_error: true
+ - cmd: kubectl -n nuvolaris delete ing --all --grace-period=0
+ ignore_error: true
+ - cmd: kubectl -n nuvolaris delete kubegres --all
+ ignore_error: true
+ - cmd: kubectl -n nuvolaris delete cm/config --grace-period=0
+ ignore_error: true
+ - cmd: kubectl delete clusterissuers/letsencrypt-issuer
+ ignore_error: true
+
+ utest:
+ cmds:
+ - |
+ for test in nuvolaris/{{.T}}*.py
+ do echo "*** [{{.KUBE}}] $test"
+ poetry run python3 -m doctest $test {{.CLI_ARGS}}
+ done
+ silent: true
+
+ iclean: rm -f deploy/*/kustomization.yaml deploy/*/__* deploy/*/*_generated.yaml
+
+ itest:
+ cmds:
+ - task: iclean
+ - |
+ kubectl apply -f deploy/nuvolaris-permissions
+ rm -f _failed.txt
+ for test in tests/{{.T}}*.ipy tests/{{.KUBE}}/{{.T}}*.ipy
+ do
+ if test -e "$test"
+ then echo "*** [{{.KUBE}}] $test"
+ rm -f deploy/*/kustomization.yaml deploy/*/__*
+ if poetry run ipython $test {{.CLI_ARGS}}
+ then echo "OK: $test"
+ else echo "FAIL: $test"
+ echo $test >>_failed.txt
+ fi
+ fi
+ done
+ if test -e _failed.txt
+ then echo "*** FAILED TESTS:"
+ cat _failed.txt
+ fi
+ silent: true
+
+ dtest:
+ cmds:
+ - task: permission
+ - task: operator
+ - task: instance
+ - task: actions
+
+ actions:
+ - task: t:config
+ - task: t:hello
+ - task: t:redis
+ - task: t:echo
+ - task: t:api
+
+ test:
+ - task: clean
+ - task: utest
+ - task: itest
+ - task: dtest
+
+ debug:
+ - poetry run ipython profile create
+ - cp test_profile.ipy ~/.ipython/profile_default/startup/
+ - task: uitest
+
+ all-kubes:
+ cmds:
+ - |-
+ if test -z "{{.CLI_ARGS}}"
+ then echo 'use "task all-kubes -- <target> runs the target against all the available kubes'
+ else for cfg in clusters/*.kubeconfig
+ do /usr/games/cowsay -f duck $(basename $cfg .kubeconfig) 2>/dev/null
+ cp -v $cfg ~/.kube/config >/dev/null
+ task {{.CLI_ARGS}}
+ done
+ fi
+ silent: true
+
+ kube-test:
+ cmds:
+ - task dtest 2>/dev/null >/dev/null
+
+ use:
+ cmds:
+ - |-
+ if test -z "{{.N}}"
+ then echo "*** current: {{.KUBE}}"
+ ls clusters/*.kubeconfig | sort | awk '{ print NR, $0 }'
+ echo "*** select with 'task #'"
+ else CFG="$(ls -1 clusters/*.kubeconfig | tail +{{.N}} | head -1)"
+ cp $CFG ~/.kube/config
+ echo "cluster: $(./detect.sh)"
+ kubectl get nodes
+ fi
+ silent: true
+
+ build-and-load:
+ - task: b:build-and-load
+
+ build-and-push:
+ - task: b:build-and-push
+
+ buildx-and-push:
+ - task: b:buildx-and-push
+
+ docker-login:
+ - task: b:docker-login
+
+ shell:
+ - kubectl -n nuvolaris exec --stdin --tty {{.POD}} -- /bin/bash
+
+ 1: task use N=1
+ 2: task use N=2
+ 3: task use N=3
+ 4: task use N=4
+ 5: task use N=5
+ 6: task use N=6
+ 7: task use N=7
+ 8: task use N=8
+ 9: task use N=9
+
diff --git a/TaskfileBuild.yml b/TaskfileBuild.yml
new file mode 100644
index 0000000..74ff8a4
--- /dev/null
+++ b/TaskfileBuild.yml
@@ -0,0 +1,63 @@
+# 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.
+#
+version: '3'
+
+vars:
+ # taken from the tag
+ OPERATOR_TAG:
+ sh: git describe --tags --abbrev=0 2>/dev/null || git rev-parse --short HEAD
+ # taken from the Dockerfile - ovverdable with MY_OPERATOR_IMAGE
+ OPERATOR_IMAGE:
+ sh: awk -F= '/ARG OPERATOR_IMAGE_DEFAULT=/ { print $2 ; exit }' Dockerfile
+
+tasks:
+ image:
+ cmds:
+ - echo "Operator=${MY_OPERATOR_IMAGE:-{{.OPERATOR_IMAGE}}}:{{.OPERATOR_TAG}}"
+ silent: true
+
+ docker-login: >
+ echo $GITHUB_TOKEN | docker login ghcr.io -u $GITHUB_USER --password-stdin
+
+ build:
+ - >
+ docker build .
+ -t ${MY_OPERATOR_IMAGE:-{{.OPERATOR_IMAGE}}}:{{.OPERATOR_TAG}}
+ --build-arg OPERATOR_IMAGE_DEFAULT=${MY_OPERATOR_IMAGE:-{{.OPERATOR_IMAGE}}}
+ --build-arg OPERATOR_TAG_DEFAULT={{.OPERATOR_TAG}} --load
+
+ build-and-push:
+ - task: docker-login
+ - task: build
+ - docker push ${MY_OPERATOR_IMAGE:-{{.OPERATOR_IMAGE}}}:{{.OPERATOR_TAG}}
+
+ build-and-load:
+ - task: build
+ - >
+ kind load docker-image
+ ${MY_OPERATOR_IMAGE:-{{.OPERATOR_IMAGE}}}:{{.OPERATOR_TAG}}
+ --name=nuvolaris
+
+ buildx-and-push:
+ - >
+ docker buildx build
+ --platform linux/amd64,linux/arm64
+ -t ${MY_OPERATOR_IMAGE:-{{.OPERATOR_IMAGE}}}:{{.OPERATOR_TAG}}
+ --build-arg OPERATOR_IMAGE_DEFAULT=${MY_OPERATOR_IMAGE:-{{.OPERATOR_IMAGE}}}
+ --build-arg OPERATOR_TAG_DEFAULT={{.OPERATOR_TAG}}
+ . --push
diff --git a/TaskfileDev.yml b/TaskfileDev.yml
new file mode 100644
index 0000000..5ea2695
--- /dev/null
+++ b/TaskfileDev.yml
@@ -0,0 +1,104 @@
+# 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.
+#
+version: '3'
+
+vars:
+ WHISK: '{{default "whisk" .WHISK}}'
+ CONFIG: "tests/{{.KUBE}}/{{.WHISK}}.yaml"
+ # taken from the tag
+ OPERATOR_TAG:
+ sh: git describe --tags --abbrev=0 2>/dev/null || git rev-parse --short HEAD
+ # taken from the Dockerfile - ovveridable with MY_OPERATOR_IMAGE
+ OPERATOR_IMAGE:
+ sh: awk -F= '/ARG OPERATOR_IMAGE_DEFAULT=/ { print $2 ; exit }' Dockerfile
+ # taken from the Dockerfile
+ CONTROLLER_TAG:
+ sh: awk -F= '/ENV CONTROLLER_TAG=/ { print $2 ; exit }' Dockerfile
+ CONTROLLER_IMAGE:
+ sh: awk -F= '/ENV CONTROLLER_IMAGE=/ { print $2 ; exit }' Dockerfile
+ # taken from the Dockerfile
+ INVOKER_TAG:
+ sh: awk -F= '/ENV INVOKER_TAG=/ { print $2 ; exit }' Dockerfile
+ INVOKER_IMAGE:
+ sh: awk -F= '/ENV INVOKER_IMAGE=/ { print $2 ; exit }' Dockerfile
+ APIKUBE:
+ sh: kubectl config view -o json | jq -r '.clusters[0].cluster.server'
+
+env:
+ APIHOST:
+ sh: echo {{.APIKUBE}} | sed -e 's|^.*//\(.*\):.*$|\1|'
+ APIHOST_OSH:
+ sh: echo {{.APIKUBE}} | sed -e 's|^.*//\(.*\):.*$|\1|' | sed -e 's/^api./nuvolaris.apps./'
+
+tasks:
+
+ env: env
+
+ cli:
+ cmds:
+ - env OPERATOR_IMAGE="${MY_OPERATOR_IMAGE:-{{.OPERATOR_IMAGE}}}" poetry run ipython -i profile.ipy
+ env:
+ CONTROLLER_IMAGE: "{{.CONTROLLER_IMAGE}}"
+ CONTROLLER_TAG: "{{.CONTROLLER_TAG}}"
+ OPERATOR_TAG: "{{.OPERATOR_TAG}}"
+
+ run:
+ cmds:
+ # note that the .env is loaded after the env pameters
+ - env OPERATOR_IMAGE="${MY_OPERATOR_IMAGE:-{{.OPERATOR_IMAGE}}}" ./run.sh
+ env:
+ CONTROLLER_IMAGE: "{{.CONTROLLER_IMAGE}}"
+ CONTROLLER_TAG: "{{.CONTROLLER_TAG}}"
+ OPERATOR_TAG: "{{.OPERATOR_TAG}}"
+ COUCHDB_SERVICE_HOST: "localhost"
+ MINIO_API_HOST: "localhost"
+ OW_CONTROLLER_HOST : "localhost"
+ OW_CONTROLLER_PORT : 3233
+ INVOKER_IMAGE: "{{.INVOKER_IMAGE}}"
+ INVOKER_TAG: "{{.INVOKER_TAG}}"
+
+ instance:
+ - envsubst <{{.CONFIG}} | kubectl -n nuvolaris apply -f -
+
+ show:
+ - echo "*** {{.CONFIG}}"
+ - envsubst <{{.CONFIG}}
+
+ destroy: kubectl -n nuvolaris delete wsk/controller
+
+ couchdb:
+ - rm -f deploy/*/kustomization.yaml deploy/*/__*
+ - task: permission
+ - kubectl apply -f deploy/couchdb
+
+ couchdb-forward:
+ - kubectl -n nuvolaris port-forward svc/couchdb 5985:5984
+
+ mongodb:
+ - kubectl apply -f deploy/mongodb-operator
+ - kubectl apply -f deploy/mongodb
+
+ defin: >
+ kubectl -n nuvolaris patch wsk/controller --type=merge --patch '{"metadata": {"finalizers":[] } }'
+
+ defin2: kubectl -n nuvolaris get wsk/controller -o yaml | grep -v Final | kubectl apply -f -
+
+ killpy: ps auwwx | grep python | awk '{print $2}' | xargs kill -9
+
+ enter: kubectl exec -ti nuvolaris-operator -- bash
+
diff --git a/TaskfileEnterprise.yml b/TaskfileEnterprise.yml
new file mode 100644
index 0000000..2a1ae28
--- /dev/null
+++ b/TaskfileEnterprise.yml
@@ -0,0 +1,71 @@
+# 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.
+#
+version: '3'
+
+vars:
+ # taken from the tag
+ OPERATOR_TAG:
+ sh: git describe --tags --abbrev=0 2>/dev/null || git rev-parse --short HEAD
+ # taken from the Dockerfile - ovverdable with MY_OPERATOR_IMAGE
+ OPERATOR_IMAGE:
+ sh: awk -F= '/ARG OPERATOR_IMAGE_DEFAULT=/ { print $2 ; exit }' Dockerfile
+
+tasks:
+ image:
+ cmds:
+ - echo "Operator=${MY_OPERATOR_IMAGE:-{{.OPERATOR_IMAGE}}}:{{.OPERATOR_TAG}}"
+ silent: true
+
+ docker-login: >
+ echo $DOCKER_HUB_TOKEN | docker login registry.hub.docker.com -u $DOCKER_HUB_USER --password-stdin
+
+ build: >
+ docker build
+ -t ${MY_OPERATOR_IMAGE:-{{.OPERATOR_IMAGE}}}:{{.OPERATOR_TAG}}
+ --build-arg=OPERATOR_IMAGE_DEFAULT=${MY_OPERATOR_IMAGE:-{{.OPERATOR_IMAGE}}}
+ --build-arg=OPERATOR_TAG_DEFAULT={{.OPERATOR_TAG}}
+ {{.CLI_ARGS}} .
+
+ build-and-push:
+ - task: docker-login
+ - task: build
+ - docker push ${MY_OPERATOR_IMAGE:-{{.OPERATOR_IMAGE}}}:{{.OPERATOR_TAG}}
+
+ build-and-load:
+ - task: build
+ - >
+ kind load docker-image
+ ${MY_OPERATOR_IMAGE:-{{.OPERATOR_IMAGE}}}:{{.OPERATOR_TAG}}
+ --name=nuvolaris
+
+ docker-setup:
+ - docker buildx create --use
+ - docker run -it --rm --privileged tonistiigi/binfmt --install all
+
+ buildx-and-push:
+ - task: docker-setup
+ - >
+ docker buildx build
+ --platform linux/amd64,linux/arm64
+ -t ${MY_OPERATOR_IMAGE:-{{.OPERATOR_IMAGE}}}:{{.OPERATOR_TAG}}
+ --build-arg OPERATOR_IMAGE_DEFAULT=${MY_OPERATOR_IMAGE:-{{.OPERATOR_IMAGE}}}
+ --build-arg OPERATOR_TAG_DEFAULT={{.OPERATOR_TAG}}
+ . --push
+
+ docker-hub-authorize: >
+ kubectl create secret docker-registry regcred --docker-server=registry.hub.docker.com --docker-username=$DOCKER_HUB_USER --docker-password=$DOCKER_HUB_TOKEN
diff --git a/TaskfileNuvEnterprise.yml b/TaskfileNuvEnterprise.yml
new file mode 100644
index 0000000..56256da
--- /dev/null
+++ b/TaskfileNuvEnterprise.yml
@@ -0,0 +1,91 @@
+# 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.
+#
+version: '3'
+
+tasks:
+ devcluster:delete:
+ - nuv ent setup devcluster --uninstall
+
+ devcluster:
+ - nuv ent config enable --prometheus --slack --quota
+ - nuv ent config slack --apiurl=$SLACK_API_URL --channel=#nuvolaris-monitoring
+ - nuv ent config license nuvolaris --registry=registry.hub.docker.com --username=$DOCKER_HUB_USER --token=$DOCKER_HUB_TOKEN
+ - nuv ent config controller --javaopts=2 --replicas=1 --loglevel=INFO
+ - nuv ent config invoker --javaopts=2 --poolmemory=4 --timeoutsrun=5 --timeoutslogs=1 --replicas=1 --loglevel=INFO
+ - nuv ent config postgres --backup --schedule='30 * * * *' --replicas=2
+ - nuv ent config minio --s3 --console
+ - nuv ent setup devcluster
+
+ devcluster:full:
+ - nuv config reset
+ - nuv config enable --all
+ - task: devcluster
+
+ devcluster:minimal:
+ - nuv config reset
+ - nuv config disable --all
+ - task: devcluster
+
+ gc-deploy:
+ - nuv ent config controller --javaopts=-Xmx2048M
+ - nuv ent config invoker --javaopts=-Xmx2048M --poolmemory=8192m
+ - nuv ent config volumes --couchdb=30 --kafka=30 --database=50 --storage=50 --alerting=20
+ - nuv ent config storage --class=rook-ceph-block --provisioner=rook-ceph.rbd.csi.ceph.com
+ - nuv ent config enable --prometheus --slack
+ - nuv ent config slack --apiurl=$SLACK_API_URL --channel=#nuvolaris-monitoring
+ - nuv ent config license nuvolaris --registry=registry.hub.docker.com --username=$DOCKER_HUB_USER --token=$DOCKER_HUB_TOKEN
+ - nuv ent setup cluster
+
+ saas1:minimal:
+ - nuv config reset
+ - nuv config disable --all
+ - nuv config apihost 146.148.119.191
+ - task: gc-deploy
+
+ saas1:full:
+ - nuv config reset
+ - nuv config enable --all
+ - nuv config apihost 146.148.119.191
+ - task: gc-deploy
+
+ nuvolaris-app:minimal:
+ - nuv config reset
+ - nuv config disable --all
+ - nuv config apihost 35.242.186.170
+ - task: gc-deploy
+
+ nuvolaris-app:full:
+ - nuv config reset
+ - nuv config enable --all
+ - nuv config apihost 35.242.186.170
+ - task: gc-deploy
+
+ gc-cluster-kube:
+ - nuv ent saas admin gc-kubeconfig {{.N}}
+
+ gc-prepare:
+ - nuv ent saas gcloud vpc-create app-vpc
+ - nuv ent saas config gcloud --ent_name=app --ent_domain=nuvolaris.app --gc_region=europe-west2 --gc_zone=europe-west2-a
+
+ gc-cluster-delete:
+ - nuv ent saas admin gc-cluster-delete {{.N}}
+
+ gc-cluster-create:
+ - nuv ent saas admin gc-cluster-create {{.N}} --token=$GITHUB_OLARIS_CLONE_TOKEN
+
+
diff --git a/TaskfileOlaris.yml b/TaskfileOlaris.yml
new file mode 100644
index 0000000..6e80ad4
--- /dev/null
+++ b/TaskfileOlaris.yml
@@ -0,0 +1,29 @@
+# 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.
+
+version: '3'
+
+env:
+ DP: deploy/nuvolaris-permissions
+ OK: olaris/kubernetes
+
+tasks:
+ copy-yaml:
+ - cp -v $DP/nuvolaris-common.yaml $DP/openwhisk-runtimes-cm.yaml $OK/common
+ - cp -v $DP/openwhisk-core-roles.yaml $DP/operator-clusterroles.yaml $DP/operator-roles.yaml $OK/roles
+ - cp -v $DP/whisk-crd.yaml $DP/whisk-user-crd.yaml $DP/workflows-crd.yaml $OK/crds
+
\ No newline at end of file
diff --git a/TaskfileTest.yml b/TaskfileTest.yml
new file mode 100644
index 0000000..645b496
--- /dev/null
+++ b/TaskfileTest.yml
@@ -0,0 +1,197 @@
+# 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.
+#
+version: '3'
+
+vars:
+ KUBE:
+ sh: ./detect.sh
+ # taken from the tag
+ OPERATOR_TAG:
+ sh: git describe --tags --abbrev=0 2>/dev/null || git rev-parse --short HEAD
+ # taken from the Dockerfile - ovverdable with MY_OPERATOR_IMAGE
+ OPERATOR_IMAGE:
+ sh: awk -F= '/ARG OPERATOR_IMAGE_DEFAULT=/ { print $2 ; exit }' Dockerfile
+
+ WHISK: '{{default "whisk" .WHISK}}'
+ CONFIG: "tests/{{.KUBE}}/{{.WHISK}}.yaml"
+ REDIS_URI: "redis://s0meP%40ass4@redis:6379"
+ REDIS_PASSWORD: s0meP@ass4
+ MINIO_HOST: "minio"
+ MINIO_PORT: 9000
+ MINIO_USER: "minioadmin"
+ MINIO_PWD : "minioadmin"
+ APIKUBE:
+ sh: kubectl config view -o json | jq -r '.clusters[0].cluster.server'
+
+ T: ""
+
+env:
+ APIHOST:
+ sh: echo {{.APIKUBE}} | sed -e 's|^.*//\(.*\):.*$|\1|'
+ APIHOST_OSH:
+ sh: echo {{.APIKUBE}} | sed -e 's|^.*//\(.*\):.*$|\1|' | sed -e 's/^api./nuvolaris.apps./'
+
+
+tasks:
+
+ kustomization:
+ cmds:
+ - |
+ cat <<__EOF__ >deploy/nuvolaris-operator/kustomization.yaml
+ apiVersion: kustomize.config.k8s.io/v1beta1
+ kind: Kustomization
+ images:
+ - name: ghcr.io/nuvolaris/nuvolaris-operator:latest
+ newName: ${MY_OPERATOR_IMAGE:-{{.OPERATOR_IMAGE}}}
+ newTag: {{.OPERATOR_TAG}}
+ resources:
+ - operator-pod.yaml
+ __EOF__
+
+ permission:
+ - kubectl apply -f deploy/nuvolaris-permissions
+
+ operator:
+ - task: kustomization
+ - kubectl apply -k deploy/nuvolaris-operator
+ - |
+ while ! kubectl -n nuvolaris wait --for=condition=ready pod/nuvolaris-operator
+ do echo still waiting...
+ done
+
+ destroy-operator:
+ - task: kustomization
+ - kubectl delete -k deploy/nuvolaris-operator
+
+ instance:
+ silent: false
+ cmds:
+ - kubectl config set-context --current --namespace nuvolaris
+ - >
+ cat tests/{{.KUBE}}/{{.WHISK}}.yaml | envsubst | kubectl apply -f -
+ - |
+ while ! kubectl -n nuvolaris wait --for=condition=ready pod/couchdb-0 2>/dev/null
+ do sleep 5 ; echo $((N++)) waiting couchdb...
+ done
+ - |
+ while ! kubectl -n nuvolaris wait --for=condition=complete job/couchdb-init 2>/dev/null
+ do sleep 5 ; echo $((N++)) waiting couchdb init...
+ done
+ - |
+ N=1
+ while ! kubectl -n nuvolaris wait --for=condition=ready pod/controller-0 2>/dev/null
+ do sleep 5 ; echo $((N++)) waiting controller...
+ done
+
+ destroy-instance:
+ - (sleep 5 ; task defin) &
+ - kubectl -n nuvolaris delete wsk/controller
+
+ destroy:
+ - task: destroy-instance
+ - task: destroy-operator
+
+ cleanup: kubectl -n nuvolaris delete pvc --all
+
+ config: |-
+ rm -f ~/.wskprops
+ while true
+ do APIHOST=$(kubectl -n nuvolaris get cm/config -o yaml | awk '/apihost:/ {print $2}')
+ if [[ `echo $APIHOST | grep pending` || -z "$APIHOST" ]];
+ then sleep 5 ; echo "$((N++)) apihost still pending..."
+ else break
+ fi
+ done
+ echo "*** $APIHOST ***"
+ AUTH=$(kubectl -n nuvolaris get wsk/controller -o jsonpath='{.spec.openwhisk.namespaces.nuvolaris}')
+ echo $AUTH
+ echo wsk property set --apihost $APIHOST --auth $AUTH
+ wsk property set --apihost $APIHOST --auth $AUTH
+ while ! wsk action list
+ do echo $(( N++)) "waiting for the load balancer to be ready..." ; sleep 10
+ done
+
+ hello:
+ - wsk -i action update hello tests/hello.js --web=true
+ - wsk -i action invoke hello -r | grep "hello"
+ - |
+ URL=$(wsk -i action get hello --url | tail +2)
+ curl -skL $URL | grep hello
+
+ redis:
+ cmds:
+ - wsk -i package update redis -p redis_url "{{.REDIS_NUV_URL}}" -p redis_prefix "{{.REDIS_NUV_PREFIX}}" -p password "{{.REDIS_PASSWORD}}"
+ - wsk -i action update redis/ping tests/ping.js
+ - wsk -i action invoke redis/ping -r | grep "PONG"
+ - wsk -i action update redis/redis tests/redis.js
+ - wsk -i action invoke redis/redis -r | grep "world"
+ vars:
+ REDIS_NUV_PREFIX:
+ sh: kubectl -n nuvolaris get cm/config -o jsonpath='{.metadata.annotations.redis_prefix}'
+ REDIS_NUV_URL:
+ sh: kubectl -n nuvolaris get cm/config -o jsonpath='{.metadata.annotations.redis_url}'
+
+ echo:
+ - wsk -i action update echo tests/echo.js -a provide-api-key true
+ - wsk -i action invoke echo -r | grep "__OW_API_KEY"
+ - wsk -i action invoke echo -r | grep "__OW_API_HOST"
+
+ api:
+ - wsk -i action update api tests/api.js -a provide-api-key true
+ - wsk -i action invoke api -r | grep '"api"'
+
+ mongo:
+ - |
+ MONGODB_URL=$(kubectl -n nuvolaris get cm/config -o jsonpath='{.metadata.annotations.mongodb_url}')
+ wsk -i package update mongo -p dburi "$MONGODB_URL"
+ - wsk -i action update mongo/mongo tests/mongo.js
+ - wsk -i action invoke mongo/mongo -r | grep "hello"
+
+ minio:
+ - wsk -i package update minio -p minio_host {{.MINIO_HOST}} -p minio_port {{.MINIO_PORT}} -p minio_user {{.MINIO_USER}} -p minio_pwd {{.MINIO_PWD}}
+ - wsk -i action update minio/minio tests/minio.js
+ - wsk -i action invoke minio/minio -r
+
+ mongo2:
+ - wsk -i project deploy --manifest tests/mongo.yaml
+
+ postgres:
+ - |
+ PG_URL=$(kubectl -n nuvolaris get cm/config -o jsonpath='{.metadata.annotations.postgres_url}')
+ wsk -i package update postgres -p dburi "$PG_URL"
+ - wsk -i action update postgres/postgres tests/postgres.js
+ - wsk -i action invoke postgres/postgres -r | grep "Nuvolaris Postgres is up and running!"
+
+ minio2:
+ silent: true
+ desc: minio test
+ cmds:
+ - |
+ MINIO_ACCESS_KEY=$(kubectl -n nuvolaris get cm/config -o jsonpath='{.metadata.annotations.minio_access_key}')
+ MINIO_SECRET_KEY=$(kubectl -n nuvolaris get cm/config -o jsonpath='{.metadata.annotations.minio_secret_key}')
+ MINIO_HOST=$(kubectl -n nuvolaris get cm/config -o jsonpath='{.metadata.annotations.minio_host}')
+ MINIO_PORT=$(kubectl -n nuvolaris get cm/config -o jsonpath='{.metadata.annotations.minio_port}')
+ MINIO_BUCKET_DATA=$(kubectl -n nuvolaris get cm/config -o jsonpath='{.metadata.annotations.minio_bucket_data}')
+ MINIO_BUCKET_WEB=$(kubectl -n nuvolaris get cm/config -o jsonpath='{.metadata.annotations.minio_bucket_static}')
+ wsk -i action update minio/minio-nuv tests/minio-nuv.js \
+ -p minio_access "$MINIO_ACCESS_KEY" \
+ -p minio_secret "$MINIO_SECRET_KEY" \
+ -p minio_host "$MINIO_HOST" \
+ -p minio_port "$MINIO_PORT" \
+ -p minio_data "$MINIO_BUCKET_DATA"
+ - wsk -i action invoke minio/minio-nuv -r
diff --git a/actions/Taskfile.yml b/actions/Taskfile.yml
new file mode 100644
index 0000000..1536972
--- /dev/null
+++ b/actions/Taskfile.yml
@@ -0,0 +1,448 @@
+# 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.
+#
+version: '3'
+
+vars:
+ KUBE:
+ sh: ../detect.sh
+ WHISK: '{{default "whisk" .WHISK}}'
+ CONFIG: "../tests/{{.KUBE}}/{{.WHISK}}.yaml"
+ APIHOST:
+ sh: kubectl -n nuvolaris get cm/config -o yaml | awk '/apihost:/ {print $2}'
+ SYSTEM:
+ sh: kubectl -n nuvolaris get wsk/controller -o jsonpath='{.spec.openwhisk.namespaces.whisk-system}'
+ WSKSYS: wsk --apihost {{.APIHOST}} -u {{.SYSTEM}}
+ DEPLOY: ../deploy/whisk-system
+
+ CDB_USER:
+ sh: kubectl -n nuvolaris get wsk/controller -o jsonpath='{.spec.couchdb.admin.user}'
+
+ CDB_PASSWORD:
+ sh: kubectl -n nuvolaris get wsk/controller -o jsonpath='{.spec.couchdb.admin.password}'
+
+ NUV_MINIO_AUTH:
+ sh: kubectl -n nuvolaris get wsk/controller -o jsonpath='{.spec.minio.nuvolaris.password}'
+
+ CDB_HOST: couchdb
+
+ MINIO_HOST: minio.nuvolaris.svc.cluster.local
+ MINIO_PORT: 9000
+
+ #GIHTUB_SA_TOKEN:
+ # sh: kubectl -n nuvolaris get secrets/nuvolaris-github-secret -o jsonpath='{.data.token}'
+
+ #GIHTUB_SA_CRT:
+ # sh: kubectl -n nuvolaris get secrets/nuvolaris-github-secret -o jsonpath='{.data.ca\.crt}'
+
+ #GIHTUB_SA_NS:
+ # sh: kubectl -n nuvolaris get secrets/nuvolaris-github-secret -o jsonpath='{.data.namespace}'
+
+tasks:
+
+ list: "{{.WSKSYS}} action list"
+
+ action:get: "{{.WSKSYS}} action get {{.NAME}}"
+
+ activation:list: "{{.WSKSYS}} activation list"
+
+ activation:get: "{{.WSKSYS}} activation get {{.ID}}"
+
+ invoke: "{{.WSKSYS}} action invoke -r {{.CLI_ARGS}}"
+
+ config: wsk property set --apihost '{{.APIHOST}}' -u '{{.SYSTEM}}'
+
+ echo:
+ - zip -j {{.DEPLOY}}/echo.zip echo/*
+ - "{{.WSKSYS}} action update echo {{.DEPLOY}}/echo.zip --kind python:3"
+
+ login:prepare:
+ - |-
+ mkdir -p login/nuvolaris
+ cp ../nuvolaris/config.py ../nuvolaris/couchdb_util.py login/nuvolaris
+ - |-
+ cd login
+ rm -f ../{{.DEPLOY}}/login.zip
+ zip -r ../{{.DEPLOY}}/login.zip *
+
+ login:deploy:
+ - >
+ {{.WSKSYS}} package update nuv
+ -p couchdb_user "{{.CDB_USER}}"
+ -p couchdb_password "{{.CDB_PASSWORD}}"
+ -p couchdb_host "{{.CDB_HOST}}"
+ - >
+ {{.WSKSYS}} action update nuv/login
+ {{.DEPLOY}}/login.zip --kind python:3 --web true
+ - >
+ {{.WSKSYS}} action get nuv/login --url
+
+ content:prepare:
+ - |-
+ mkdir -p content/common
+ cp ./common/minio_util.py content/common
+ - |-
+ cd content
+ rm -f ../{{.DEPLOY}}/content.zip
+ zip -r ../{{.DEPLOY}}/content.zip *
+
+ content:deploy:
+ - task: content:prepare
+ - >
+ {{.WSKSYS}} package update nuv
+ -p minio_host {{.MINIO_HOST}}
+ -p minio_port {{.MINIO_PORT}}
+ - >
+ {{.WSKSYS}} action update nuv/content
+ {{.DEPLOY}}/content.zip --kind python:3 --web true
+ - >
+ {{.WSKSYS}} action get nuv/content --url
+
+ devel:redis:prepare:
+ - |-
+ mkdir -p devel/redis/nuvolaris
+ mkdir -p devel/redis/common
+ cp ./common/*.py devel/redis/common
+ cp ../nuvolaris/config.py ../nuvolaris/couchdb_util.py devel/redis/nuvolaris
+ - |-
+ cd devel/redis
+ rm -f ../../{{.DEPLOY}}/redis.zip
+ zip -r ../../{{.DEPLOY}}/redis.zip *
+
+ devel:redis:deploy:
+ - task: devel:redis:prepare
+ - >
+ {{.WSKSYS}} package update nuv
+ -p couchdb_user "{{.CDB_USER}}"
+ -p couchdb_password "{{.CDB_PASSWORD}}"
+ -p couchdb_host "{{.CDB_HOST}}"
+ - >
+ {{.WSKSYS}} action update nuv/redis
+ {{.DEPLOY}}/redis.zip --kind python:3 --web raw
+ - >
+ {{.WSKSYS}} action get nuv/redis --url
+
+ devel:psql:prepare:
+ - |-
+ mkdir -p devel/psql/nuvolaris
+ mkdir -p devel/psql/common
+ cp ./common/*.py devel/psql/common
+ cp ../nuvolaris/config.py ../nuvolaris/couchdb_util.py devel/psql/nuvolaris
+ - |-
+ cd devel/psql
+ rm -f ../../{{.DEPLOY}}/psql.zip
+ zip -r ../../{{.DEPLOY}}/psql.zip *
+
+ devel:psql:deploy:
+ - task: devel:psql:prepare
+ - >
+ {{.WSKSYS}} package update nuv
+ -p couchdb_user "{{.CDB_USER}}"
+ -p couchdb_password "{{.CDB_PASSWORD}}"
+ -p couchdb_host "{{.CDB_HOST}}"
+ - >
+ {{.WSKSYS}} action update nuv/psql
+ {{.DEPLOY}}/psql.zip --kind python:3 --web raw
+ - >
+ {{.WSKSYS}} action get nuv/psql --url
+
+ devel:minio:prepare:
+ - |-
+ mkdir -p devel/minio/nuvolaris
+ mkdir -p devel/minio/common
+ cp ./common/*.py devel/minio/common
+ cp ../nuvolaris/config.py ../nuvolaris/couchdb_util.py devel/minio/nuvolaris
+ - |-
+ cd devel/minio
+ rm -f ../../{{.DEPLOY}}/minio.zip
+ zip -r ../../{{.DEPLOY}}/minio.zip *
+
+ devel:minio:deploy:
+ - task: devel:minio:prepare
+ - >
+ {{.WSKSYS}} package update nuv
+ -p couchdb_user "{{.CDB_USER}}"
+ -p couchdb_password "{{.CDB_PASSWORD}}"
+ -p couchdb_host "{{.CDB_HOST}}"
+ - >
+ {{.WSKSYS}} action update nuv/minio
+ {{.DEPLOY}}/minio.zip --kind python:3 --web raw
+ - >
+ {{.WSKSYS}} action get nuv/minio --url
+
+ devel:upload:prepare:
+ - |-
+ mkdir -p devel/upload/nuvolaris
+ mkdir -p devel/upload/common
+ cp ./common/*.py devel/upload/common
+ cp ../nuvolaris/config.py ../nuvolaris/couchdb_util.py devel/upload/nuvolaris
+ - |-
+ cd devel/upload
+ rm -f ../../{{.DEPLOY}}/devel_upload.zip
+ zip -r ../../{{.DEPLOY}}/devel_upload.zip *
+
+ devel:upload:deploy:
+ - task: devel:upload:prepare
+ - >
+ {{.WSKSYS}} package update nuv
+ -p couchdb_user "{{.CDB_USER}}"
+ -p couchdb_password "{{.CDB_PASSWORD}}"
+ -p couchdb_host "{{.CDB_HOST}}"
+ - >
+ {{.WSKSYS}} action update nuv/devel_upload
+ {{.DEPLOY}}/devel_upload.zip --kind python:3 --web raw
+ - >
+ {{.WSKSYS}} action get nuv/devel_upload --url
+
+ devel:ferretdb:prepare:
+ - |-
+ mkdir -p devel/ferretdb/nuvolaris
+ mkdir -p devel/ferretdb/common
+ cp ./common/*.py devel/ferretdb/common
+ cp ../nuvolaris/config.py ../nuvolaris/couchdb_util.py devel/ferretdb/nuvolaris
+ - |-
+ cd devel/ferretdb
+ rm -f ../../{{.DEPLOY}}/ferretdb.zip
+ zip -r ../../{{.DEPLOY}}/ferretdb.zip *
+
+ devel:ferretdb:deploy:
+ - task: devel:ferretdb:prepare
+ - >
+ {{.WSKSYS}} package update nuv
+ -p couchdb_user "{{.CDB_USER}}"
+ -p couchdb_password "{{.CDB_PASSWORD}}"
+ -p couchdb_host "{{.CDB_HOST}}"
+ - >
+ {{.WSKSYS}} action update nuv/ferretdb
+ {{.DEPLOY}}/ferretdb.zip --kind python:3 --web raw
+ - >
+ {{.WSKSYS}} action get nuv/ferretdb --url
+
+ devel:download:prepare:
+ - |-
+ mkdir -p devel/download/nuvolaris
+ mkdir -p devel/download/common
+ cp ./common/*.py devel/download/common
+ cp ../nuvolaris/config.py ../nuvolaris/couchdb_util.py devel/download/nuvolaris
+ - |-
+ cd devel/download
+ rm -f ../../{{.DEPLOY}}/devel_download.zip
+ zip -r ../../{{.DEPLOY}}/devel_download.zip *
+
+ devel:download:deploy:
+ - task: devel:download:prepare
+ - >
+ {{.WSKSYS}} package update nuv
+ -p couchdb_user "{{.CDB_USER}}"
+ -p couchdb_password "{{.CDB_PASSWORD}}"
+ -p couchdb_host "{{.CDB_HOST}}"
+ - >
+ {{.WSKSYS}} action update nuv/devel_download
+ {{.DEPLOY}}/devel_download.zip --kind python:3 --web raw
+ - >
+ {{.WSKSYS}} action get nuv/devel_download --url
+
+ login:webtest:
+ - >
+ curl -X POST {{.APIHOST}}/api/v1/web/whisk-system/nuv/login -H "Content-Type: application/json" -d '{"login": "{{.USERNAME}}", "password": "{{.PASSWORD}}"}'
+
+ login:webproxy:
+ - >
+ curl -X POST http://localhost/api/v1/web/whisk-system/nuv/login -H "Content-Type: application/json" -d '{"login": "{{.USERNAME}}", "password": "{{.PASSWORD}}"}'
+
+ login:k3sproxy:
+ - >
+ curl -X POST https://gptuser.api.k3s.nuvtest.net/api/v1/web/whisk-system/nuv/login -H "Content-Type: application/json" -d '{"login": "{{.USERNAME}}", "password": "{{.PASSWORD}}"}'
+
+ content:webtest:
+ - >
+ curl -X PUT -T ../nuvolaris/templates/index.html -H "minioauth: {{.NUV_MINIO_AUTH}}" {{.APIHOST}}/api/v1/web/whisk-system/nuv/content/nuvolaris/index2.html
+
+ content:webtest2:
+ - >
+ curl -X PUT -T ../nuvolaris/templates/content.html -H "minioauth: {{.NUV_MINIO_AUTH}}" {{.APIHOST}}/api/v1/web/whisk-system/nuv/content/nuvolaris/tidy/content.html
+
+ content:yaml:
+ - >
+ curl -X PUT -T ../nuvolaris/templates/couchdb-init.yaml -H "minioauth: {{.NUV_MINIO_AUTH}}" {{.APIHOST}}/api/v1/web/whisk-system/nuv/content/nuvolaris/yaml/couchdb-init.yaml
+
+ content-delete:yaml:
+ - >
+ curl -X DELETE -H "minioauth: {{.NUV_MINIO_AUTH}}" {{.APIHOST}}/api/v1/web/whisk-system/nuv/content/nuvolaris/yaml/couchdb-init.yaml
+
+ content:chess:
+ - >
+ curl -X PUT -T ../nuvolaris/templates/content.html -H "minioauth: {{.NUV_MINIO_AUTH}}" {{.APIHOST}}/api/v1/web/whisk-system/nuv/content/nuvolaris/chess/index.html
+
+ devel:redis:set:
+ - >
+ curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/redis -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/redis_set.json
+
+ devel:redis:get:
+ - >
+ curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/redis -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/redis_get.json
+
+ devel:psql:select:
+ - >
+ curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/psql -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/psql_select.json
+
+ devel:psql:desc:
+ - >
+ curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/psql -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/psql_desc.json
+
+ devel:psql:create:
+ - >
+ curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/psql -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/psql_create1.json
+ - >
+ curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/psql -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/psql_create2.json
+ - >
+ curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/psql -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/psql_create3.json
+
+ devel:minio:ls:
+ - >
+ curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/minio -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/minio_buckets.json
+
+ devel:minio:lsb:
+ - >
+ curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/minio -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/minio_ls.json
+
+ devel:minio:rm:
+ - >
+ curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/minio -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/minio_rm.json
+
+ devel:minio:mv:
+ - >
+ curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/minio -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/minio_mv.json
+
+ devel:minio:cp:
+ - >
+ curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/minio -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/minio_cp.json
+
+ devel:upload:webtest:
+ - >
+ curl -X PUT -T ../nuvolaris/templates/content.html -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" {{.APIHOST}}/api/v1/web/whisk-system/nuv/devel_upload/nuvolaris-web/test/index.html
+
+ devel:upload:data:
+ - >
+ curl -X PUT -T ../nuvolaris/templates/content.html -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" {{.APIHOST}}/api/v1/web/whisk-system/nuv/devel_upload/nuvolaris-data/test/index.html
+
+ devel:ferretdb:insert:
+ - >
+ curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/ferretdb -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/mdb_insert.json
+
+ devel:ferretdb:find:
+ - >
+ curl {{.APIHOST}}/api/v1/web/whisk-system/nuv/ferretdb -H "x-impersonate-auth: Y2JkNjgwNzUtZGFjMi00NzVlLThjMDctZDYyYTMwYzdlNjgzOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" -H "Content-Type: application/json" -d@../demos/devel/mdb_find.json
+
+
+
+ login:all:
+ - task: login:prepare
+ - task: login:deploy
+ - task: login:webtest
+
+ content:all:
+ - task: content:prepare
+ - task: content:deploy
+
+ # prepares all the system related actions
+ prepare:
+ - task: login:prepare
+ - task: content:prepare
+
+ deploy:
+ - task: login:deploy
+ - task: content:deploy
+
+ all:
+ - task: prepare
+ - task: deploy
+
+ deploy:devel:
+ - task: devel:redis:deploy
+ - task: devel:minio:deploy
+ - task: devel:psql:deploy
+ - task: devel:upload:deploy
+ - task: devel:ferretdb:deploy
+ - task: devel:download:deploy
+
+ github:echo:
+ silent: true
+ cmds:
+ - echo "{{.GIHTUB_SA_TOKEN}}"
+ - echo "{{.GIHTUB_SA_NS}}"
+ - echo "{{.GIHTUB_SA_CRT}}"
+
+ github:package:deploy:
+ - >
+ {{.WSKSYS}} package update github
+ -p couchdb_user "{{.CDB_USER}}"
+ -p couchdb_passwo986149f29564be9d0543a11d6657bca63deb7ff5rd "{{.CDB_PASSWORD}}"
+ -p couchdb_host "{{.CDB_HOST}}"
+ -p sa_token "{{.GIHTUB_SA_TOKEN}}"
+ -p sa_crt "{{.GIHTUB_SA_CRT}}"
+ -p sa_ns "{{.GIHTUB_SA_NS}}"
+ -p client_id "Iv1.d2bc07e358e18a70"
+ -p client_secret "986149f29564be9d0543a11d6657bca63deb7ff5"
+ -p kube_host "10.43.0.1"
+ -p kube_port "443"
+ -p apihost "api.arm.nuvtest.net"
+
+
+ github:callback:prepare:
+ - |-
+ mkdir -p github/callback/nuvolaris
+ mkdir -p github/callback/common
+ cp github/common/*.py github/callback/common
+ cp ../nuvolaris/config.py ../nuvolaris/couchdb_util.py github/callback/nuvolaris
+ - |-
+ cd github/callback
+ rm -f ../../{{.DEPLOY}}/callback.zip
+ zip -r ../../{{.DEPLOY}}/callback.zip *
+
+ github:callback:deploy:
+ - task: github:callback:prepare
+ - >
+ {{.WSKSYS}} action update github/callback
+ {{.DEPLOY}}/callback.zip --kind python:3 --web true
+ - >
+ {{.WSKSYS}} action get github/callback --url
+
+ github:webhook:prepare:
+ - |-
+ mkdir -p github/webhook/nuvolaris
+ mkdir -p github/webhook/common
+ cp github/common/*.py github/webhook/common
+ cp ../nuvolaris/config.py ../nuvolaris/couchdb_util.py github/webhook/nuvolaris
+ - |-
+ cd github/webhook
+ rm -f ../../{{.DEPLOY}}/webhook.zip
+ zip -r ../../{{.DEPLOY}}/webhook.zip *
+
+ github:webhook:deploy:
+ - task: github:webhook:prepare
+ - >
+ {{.WSKSYS}} action update github/webhook
+ {{.DEPLOY}}/webhook.zip --kind python:3 --web raw
+ - >
+ {{.WSKSYS}} action get github/webhook --url
+
+ deploy:github:
+ - task: github:package:deploy
+ - task: github:callback:deploy
+ - task: github:webhook:deploy
+
diff --git a/actions/common/authorize.py b/actions/common/authorize.py
new file mode 100644
index 0000000..f7d716f
--- /dev/null
+++ b/actions/common/authorize.py
@@ -0,0 +1,162 @@
+# 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.
+#
+from base64 import b64decode, b64encode
+from urllib.parse import quote, unquote
+
+import nuvolaris.config as cfg
+import nuvolaris.couchdb_util as cu
+import json
+import common.util as ut
+
+USER_META_DBN = "users_metadata"
+SUBJECT_META_DBN = "subjects"
+
+class DecodeError(Exception):
+ pass
+
+class EncodeError(Exception):
+ pass
+
+class AuthorizationError(Exception):
+ pass
+
+class Authorize():
+
+ def __init__(self, cdb_host, cdb_user, cdb_pwd):
+ cfg.clean()
+ cfg.put("couchdb.host", cdb_host)
+ cfg.put("couchdb.admin.user", cdb_user)
+ cfg.put("couchdb.admin.password", cdb_pwd)
+ self._db = cu.CouchDB()
+
+ def encode(self,username, password):
+ """Returns an HTTP basic authentication encrypted string given a valid
+ username and password.
+ """
+ if ':' in username:
+ raise EncodeError
+
+ username_password = f'{quote(username)}:{quote(password)}'
+ return f'Basic {b64encode(username_password.encode()).decode()}'
+
+ def _parse_b64(self, encoded_str):
+ try:
+ username, password = b64decode(encoded_str).decode().split(':', 1)
+ except:
+ # fallback in case the token is not bas64 encoded
+ username, password = encoded_str.split(':', 1)
+
+ if not username or not password:
+ raise DecodeError("authentication token does not seems to be b64 encoded")
+
+ return username, password
+
+ def decode(self, encoded_str):
+ """Decode an encrypted HTTP basic authentication string. Returns a tuple of
+ the form (username, password), and raises a DecodeError exception if
+ nothing could be decoded.
+ """
+ split = encoded_str.strip().split(' ')
+
+ # If split is only one element, try to decode the username and password
+ # directly.
+ if len(split) == 1:
+ username, password = self._parse_b64(split[0])
+
+ # If there are only two elements, check the first and ensure it says
+ # 'basic' so that we know we're about to decode the right thing. If not,
+ # bail out.
+ elif len(split) == 2:
+ if split[0].strip().lower() == 'basic':
+ username, password = self._parse_b64(split[1])
+ else:
+ raise DecodeError("authentication token provides more than 2 elements. could not parse properly")
+
+ # If there are more than 2 elements, something crazy must be happening.
+ # Bail.
+ else:
+ raise DecodeError("unpredictable error parsing authentication token")
+
+ return unquote(username), unquote(password)
+
+ def fetch_subject(self, uuid: str, key: str):
+ """
+ Query the internal couchdb searching for the subject mathing the given uuid, key.
+ Normally these stored in wsk or wsku in the form uuid:key
+ :param uuid the OW subject uuid
+ :param key the OW subject key
+ :return a ubject document
+ """
+ print(f"searching for openwhisk subject {uuid}")
+ try:
+ selector= {
+ "selector": {"namespaces": {"$elemMatch": {"uuid": uuid,"key": key}}}
+ }
+
+ response = self._db.find_doc(SUBJECT_META_DBN, json.dumps(selector))
+
+ if(response['docs']):
+ docs = list(response['docs'])
+ if(len(docs) > 0):
+ print(f"Nuvolaris namespace for user {uuid} found. Returning Result.")
+ return docs[0]
+
+ print(f"Nuvolaris metadata for user {uuid} not found!")
+ return None
+ except Exception as e:
+ print(f"failed to query Nuvolaris metadata for user {uuid}. Reason: {e}")
+ return None
+
+ def fetch_user_data(self, username: str):
+ """
+ Query the internal couchdb searching for the given principal to retrieve all the
+ relevant metadata
+ """
+ print(f"searching for user {username} meta-data")
+ try:
+ selector = {"selector":{"login": {"$eq": username }}}
+ response = self._db.find_doc(USER_META_DBN, json.dumps(selector))
+
+ if(response['docs']):
+ docs = list(response['docs'])
+ if(len(docs) > 0):
+ print(f"Nuvolaris metadata for user {username} found. Returning Result.")
+ return docs[0]
+
+ print(f"Nuvolaris metadata for user {username} not found!")
+ return None
+ except Exception as e:
+ print(f"failed to query Nuvolaris metadata for user {username}. Reason: {e}")
+ return None
+
+ def login(self, authorization: str):
+ """
+ Attempt to login the user identified by the given Openwhisk authorization AUTH token as base64
+ """
+ uuid, key = self.decode(authorization)
+ subject = self.fetch_subject(uuid, key)
+
+ if not subject:
+ raise AuthorizationError("Openwhisk subject not found.")
+
+ user_data = self.fetch_user_data(subject['subject'])
+
+ if user_data:
+ return user_data
+
+ raise AuthorizationError("Could not retrieve user metadata.")
\ No newline at end of file
diff --git a/actions/common/command_data.py b/actions/common/command_data.py
new file mode 100644
index 0000000..44f3faa
--- /dev/null
+++ b/actions/common/command_data.py
@@ -0,0 +1,50 @@
+# 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 json
+import logging
+
+class CommandData:
+ _data = {}
+ _raw_data = {}
+
+ def __init__(self, cmd):
+ self._data = cmd
+ self._raw_data = cmd.copy()
+
+ def dump(self):
+ logging.debug(json.dumps(self._data))
+
+ def status(self, status):
+ self._data['status']=status
+
+ def result(self, result):
+ self._data['result']=result
+
+ def get_metadata(self):
+ return self._data
+
+ def get_raw_data(self):
+ return self._raw_data
+
+ def command(self):
+ return self._data['command']
+
+ def args(self):
+ return self._data['args']
+
+
diff --git a/actions/common/minio_util.py b/actions/common/minio_util.py
new file mode 100644
index 0000000..e1e8f90
--- /dev/null
+++ b/actions/common/minio_util.py
@@ -0,0 +1,182 @@
+# 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 os
+import base64
+import random
+import string
+import mimetypes
+
+from minio import Minio
+from minio.error import S3Error
+from minio.commonconfig import CopySource
+
+def extract_mimetype(file):
+ mimetype, _ = mimetypes.guess_type(file)
+
+ if mimetype is None:
+ return "application/octet-stream"
+ else:
+ return mimetype
+
+def build_mo_client(host, port, access_key, secret_key):
+ """
+ Creates an Minio client pointing to the given MINIO HOST
+ :param host, minio host
+ :param port, minio api port normally it is the 9000
+ :param access_key user we are representing
+ :param secret_key to access minio
+ """
+ mo_client = Minio(f"{host}:{port}",access_key=access_key,secret_key=secret_key,secure=False)
+ return mo_client
+
+def upload_file(mo_client, file, bucket, object_name=None):
+ """Upload a file to an S3 bucket
+
+ :param file_name: File to upload
+ :param bucket: Bucket to upload to
+ :param object_name: S3 object name. If not specified then file_name is used
+ :return: a tuple with the Booleand str,
+ """
+
+ # If S3 object_name was not specified, use file_name
+ if object_name is None:
+ object_name = os.path.basename(file)
+
+ print(f"uploading {object_name} into bucket {bucket} from tmp_file {file}")
+
+ # Upload the file
+ try:
+ mimetype = extract_mimetype(object_name)
+ response = mo_client.fput_object(bucket_name=bucket,object_name=object_name,file_path=file,content_type=mimetype)
+ if response._object_name:
+ return True, response._object_name
+ except Exception as e:
+ print(e)
+ return False, str(e)
+ return False, "KO"
+
+def prepare_file_upload(username, filename, file_content_as_b64):
+ """ Creates a tmp area for the given user where the uploaded file will be stored under a random generated name
+ the fully qualified filename is taken into account only on the corresponding destination bucket.
+ param: username
+ param: filename
+ param: file_content_as_b64
+ return: a file object pointing to the tmp file
+ """
+ try:
+ user_tmp_folder = f"/tmp/{username}"
+ if not os.path.exists(user_tmp_folder):
+ os.makedirs(user_tmp_folder)
+ print(f"added tmp folder {user_tmp_folder}")
+
+ delete_files_in_directory_and_subdirectories(user_tmp_folder)
+ rnd_filename = get_random_string(20)
+ tmp_file = f"{user_tmp_folder}/{rnd_filename}"
+
+ with open(tmp_file, "wb") as f:
+ file_content=base64.b64decode(file_content_as_b64)
+ f.write(file_content)
+
+ if os.path.exists(tmp_file):
+ print(f"{tmp_file} stored successfully.")
+ return tmp_file
+ else:
+ return None
+ except Exception as e:
+ print("error preparing tmp_files",e)
+ return None
+
+def delete_files_in_directory_and_subdirectories(directory_path):
+ try:
+ for root, dirs, files in os.walk(directory_path):
+ for file in files:
+ file_path = os.path.join(root, file)
+ os.remove(file_path)
+ print("All files and subdirectories deleted successfully.")
+ except OSError:
+ print("Error occurred while deleting files and subdirectories.")
+
+def get_random_string(length):
+ # choose from all lowercase letter
+ letters = string.ascii_lowercase
+ return ''.join(random.choice(letters) for i in range(length))
+
+def rm_file(mo_client, bucket, file):
+ """ Remove a file from a bucket
+ :parma mo_client a minio client instance to execute the command
+ :param bucket the bucket name
+ :param file the file name
+ :return True if the file has been removed, False otherwise
+ """
+
+ try:
+ mo_client.remove_object(bucket, file)
+ return True
+ except Exception as e:
+ print(e)
+ return False
+ return False
+
+def cp_file(mo_client, orig_bucket, orig_file, dest_bucket, dest_file):
+ """Copy a file between two buckets
+
+ :param orig_bucket: Origin bucket
+ :param orig_file: Origin file
+ :param dest_bucket: Destination bucket
+ :param dest_file: Destination file
+ :return: name of the destination file is everything is ok. None otherwise
+ """
+ print(f"**** moving file {orig_file} from bucket {orig_bucket} to bucket {dest_bucket} with name {dest_file}")
+ try:
+ # copy an object from a bucket to another.
+ cp_result = mo_client.copy_object(
+ dest_bucket,
+ dest_file,
+ CopySource(orig_bucket, orig_file)
+ )
+ if cp_result.object_name:
+ return cp_result.object_name
+ except Exception as e:
+ print(e)
+ return None
+ return None
+
+def mv_file(mo_client, orig_bucket, orig_file, dest_bucket, dest_file):
+ """Move a file between two buckets
+
+ :param orig_bucket: Origin bucket
+ :param orig_file: Origin file
+ :param dest_bucket: Destination bucket
+ :param dest_file: Destination file
+ :return: name of the destination file is everything is ok. None otherwise
+ """
+ print(f"**** moving file {orig_file} from bucket {orig_bucket} to bucket {dest_bucket} with name {dest_file}")
+
+ # Upload the file
+ try:
+ # mv an object from a bucket to another.
+ object_name = cp_file(mo_client, orig_bucket, orig_file, dest_bucket, dest_file)
+ if object_name:
+ rm_file(mo_client,orig_bucket,orig_file)
+ return object_name
+ except Exception as e:
+ print(e)
+ return None
+ return None
+
diff --git a/actions/common/util.py b/actions/common/util.py
new file mode 100644
index 0000000..f1e8f39
--- /dev/null
+++ b/actions/common/util.py
@@ -0,0 +1,35 @@
+# 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 mimetypes
+
+def get_env_value(user_data, key):
+ """
+ Check if inside the given user_data object there is an env item with the
+ given name and returns the value if any.
+ :param user_data
+ :param key
+ :return None if the given key it is not prese
+ """
+ envs = list(user_data['env'])
+
+ for env in envs:
+ if env['key'] == key:
+ return env['value']
+
+ return None
\ No newline at end of file
diff --git a/actions/content/__main__.py b/actions/content/__main__.py
new file mode 100644
index 0000000..908868a
--- /dev/null
+++ b/actions/content/__main__.py
@@ -0,0 +1,110 @@
+import os
+import base64
+import random
+import string
+import mimetypes
+
+import common.minio_util as mutil
+
+def build_error(message: str):
+ return {
+ "statusCode": 400,
+ "body": message
+ }
+
+def build_response(user, filename, bucket, upload_result, operation):
+ body = {
+ "user":user,
+ "filename":filename,
+ "bucket":bucket,
+ "operation": operation
+ }
+
+ return {
+ "statusCode": upload_result and 200 or 400,
+ "body": body
+ }
+
+
+def process_path_param(ow_path: str):
+ """ Process the __ow_path parameters to extract username and fully qualified filename
+
+ :parma ow_path, the __ow_path parameter passed by openwhisk action controller
+ :return a dictionary {user:<user>, filename:<filepath>}
+ """
+ path_params = ow_path
+ if ow_path.startswith("/"):
+ path_params = ow_path[1:None]
+
+ path_elements = path_params.split("/")
+
+ upload_data = {}
+
+ if len(path_elements) >= 1:
+ upload_data['user']=path_elements[0]
+ upload_data['filename']=path_params.replace(f"{upload_data['user']}/","")
+
+ return upload_data
+
+def _put(mo_client, upload_data, content_as_b64):
+ """
+ Store the given payload into a folder
+ :param mo_client
+ :param upload_data
+ :param content_as_b64 file to be stored in a base64 endoded format
+ """
+ print(f"processing request to upload file {upload_data['filename']} under {upload_data['user']} web-bucket")
+ tmp_file = mutil.prepare_file_upload(upload_data['user'],upload_data['filename'],content_as_b64)
+
+ if tmp_file:
+ upload_result, upload_message = mutil.upload_file(mo_client,tmp_file,f"{upload_data['user']}-web",upload_data['filename'])
+ return build_response(upload_data['user'],upload_data['filename'],f"{upload_data['user']}-web",upload_result,"PUT")
+ else:
+ return build_error("Unexptected error content action. Check activation log")
+
+def _delete(mo_client, upload_data):
+ print(f"processing request to remove file {upload_data['filename']} under {upload_data['user']} web-bucket")
+ removed = mutil.rm_file(mo_client,f"{upload_data['user']}-web", upload_data['filename'])
+
+ if removed:
+ return build_response(upload_data['user'],upload_data['filename'],f"{upload_data['user']}-web",removed,"DELETE")
+ else:
+ return build_error("Unexptected error content action. Check activation log")
+
+def main(args):
+ """
+ Simple actions to upload/remove a files into a minio bucket.
+ The action it is supposed to receive a path param similar to /<user>/<path>?<auth>
+ and will store/remove the given path under the <user>-web bucket using minio client
+ """
+ print(args)
+
+ method = args['__ow_method']
+ headers = args['__ow_headers']
+
+ if(method.lower() not in ['put','delete'] ):
+ return build_error(f"invalid request, HTTP verb {method} is not supported")
+
+ if('minioauth' not in headers):
+ return build_error("invalid request, missing mandatory header: minioauth")
+
+ upload_data = process_path_param(args['__ow_path'])
+
+ if 'user' not in upload_data and 'filename' not in upload_data:
+ return build_error("invalid request, username and/or filename path error")
+
+ minio_host = args['minio_host']
+ minio_port = args['minio_port']
+ auth = headers['minioauth']
+
+ mo_client = mutil.build_mo_client(minio_host, minio_port,upload_data['user'], auth)
+
+ if method.lower() in 'put':
+ if(len(args['__ow_body']) == 0):
+ return build_error("invalid request, no file content has been received")
+
+ content_as_b64 = args['__ow_body']
+ return _put(mo_client, upload_data, content_as_b64)
+
+ if method.lower() in 'delete':
+ return _delete(mo_client, upload_data)
diff --git a/actions/content/common/minio_util.py b/actions/content/common/minio_util.py
new file mode 100644
index 0000000..e1e8f90
--- /dev/null
+++ b/actions/content/common/minio_util.py
@@ -0,0 +1,182 @@
+# 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 os
+import base64
+import random
+import string
+import mimetypes
+
+from minio import Minio
+from minio.error import S3Error
+from minio.commonconfig import CopySource
+
+def extract_mimetype(file):
+ mimetype, _ = mimetypes.guess_type(file)
+
+ if mimetype is None:
+ return "application/octet-stream"
+ else:
+ return mimetype
+
+def build_mo_client(host, port, access_key, secret_key):
+ """
+ Creates an Minio client pointing to the given MINIO HOST
+ :param host, minio host
+ :param port, minio api port normally it is the 9000
+ :param access_key user we are representing
+ :param secret_key to access minio
+ """
+ mo_client = Minio(f"{host}:{port}",access_key=access_key,secret_key=secret_key,secure=False)
+ return mo_client
+
+def upload_file(mo_client, file, bucket, object_name=None):
+ """Upload a file to an S3 bucket
+
+ :param file_name: File to upload
+ :param bucket: Bucket to upload to
+ :param object_name: S3 object name. If not specified then file_name is used
+ :return: a tuple with the Booleand str,
+ """
+
+ # If S3 object_name was not specified, use file_name
+ if object_name is None:
+ object_name = os.path.basename(file)
+
+ print(f"uploading {object_name} into bucket {bucket} from tmp_file {file}")
+
+ # Upload the file
+ try:
+ mimetype = extract_mimetype(object_name)
+ response = mo_client.fput_object(bucket_name=bucket,object_name=object_name,file_path=file,content_type=mimetype)
+ if response._object_name:
+ return True, response._object_name
+ except Exception as e:
+ print(e)
+ return False, str(e)
+ return False, "KO"
+
+def prepare_file_upload(username, filename, file_content_as_b64):
+ """ Creates a tmp area for the given user where the uploaded file will be stored under a random generated name
+ the fully qualified filename is taken into account only on the corresponding destination bucket.
+ param: username
+ param: filename
+ param: file_content_as_b64
+ return: a file object pointing to the tmp file
+ """
+ try:
+ user_tmp_folder = f"/tmp/{username}"
+ if not os.path.exists(user_tmp_folder):
+ os.makedirs(user_tmp_folder)
+ print(f"added tmp folder {user_tmp_folder}")
+
+ delete_files_in_directory_and_subdirectories(user_tmp_folder)
+ rnd_filename = get_random_string(20)
+ tmp_file = f"{user_tmp_folder}/{rnd_filename}"
+
+ with open(tmp_file, "wb") as f:
+ file_content=base64.b64decode(file_content_as_b64)
+ f.write(file_content)
+
+ if os.path.exists(tmp_file):
+ print(f"{tmp_file} stored successfully.")
+ return tmp_file
+ else:
+ return None
+ except Exception as e:
+ print("error preparing tmp_files",e)
+ return None
+
+def delete_files_in_directory_and_subdirectories(directory_path):
+ try:
+ for root, dirs, files in os.walk(directory_path):
+ for file in files:
+ file_path = os.path.join(root, file)
+ os.remove(file_path)
+ print("All files and subdirectories deleted successfully.")
+ except OSError:
+ print("Error occurred while deleting files and subdirectories.")
+
+def get_random_string(length):
+ # choose from all lowercase letter
+ letters = string.ascii_lowercase
+ return ''.join(random.choice(letters) for i in range(length))
+
+def rm_file(mo_client, bucket, file):
+ """ Remove a file from a bucket
+ :parma mo_client a minio client instance to execute the command
+ :param bucket the bucket name
+ :param file the file name
+ :return True if the file has been removed, False otherwise
+ """
+
+ try:
+ mo_client.remove_object(bucket, file)
+ return True
+ except Exception as e:
+ print(e)
+ return False
+ return False
+
+def cp_file(mo_client, orig_bucket, orig_file, dest_bucket, dest_file):
+ """Copy a file between two buckets
+
+ :param orig_bucket: Origin bucket
+ :param orig_file: Origin file
+ :param dest_bucket: Destination bucket
+ :param dest_file: Destination file
+ :return: name of the destination file is everything is ok. None otherwise
+ """
+ print(f"**** moving file {orig_file} from bucket {orig_bucket} to bucket {dest_bucket} with name {dest_file}")
+ try:
+ # copy an object from a bucket to another.
+ cp_result = mo_client.copy_object(
+ dest_bucket,
+ dest_file,
+ CopySource(orig_bucket, orig_file)
+ )
+ if cp_result.object_name:
+ return cp_result.object_name
+ except Exception as e:
+ print(e)
+ return None
+ return None
+
+def mv_file(mo_client, orig_bucket, orig_file, dest_bucket, dest_file):
+ """Move a file between two buckets
+
+ :param orig_bucket: Origin bucket
+ :param orig_file: Origin file
+ :param dest_bucket: Destination bucket
+ :param dest_file: Destination file
+ :return: name of the destination file is everything is ok. None otherwise
+ """
+ print(f"**** moving file {orig_file} from bucket {orig_bucket} to bucket {dest_bucket} with name {dest_file}")
+
+ # Upload the file
+ try:
+ # mv an object from a bucket to another.
+ object_name = cp_file(mo_client, orig_bucket, orig_file, dest_bucket, dest_file)
+ if object_name:
+ rm_file(mo_client,orig_bucket,orig_file)
+ return object_name
+ except Exception as e:
+ print(e)
+ return None
+ return None
+
diff --git a/actions/devel/download/__main__.py b/actions/devel/download/__main__.py
new file mode 100644
index 0000000..080f45b
--- /dev/null
+++ b/actions/devel/download/__main__.py
@@ -0,0 +1,91 @@
+import os
+import base64
+import random
+import string
+import mimetypes
+import io
+
+import common.minio_util as mutil
+import common.util as ut
+
+from common.authorize import Authorize
+
+def build_error(message: str):
+ return {
+ "statusCode": 400,
+ "body": message
+ }
+
+def build_response(filename, content_type, buffer):
+ return {
+ "statusCode": 200,
+ "headers": {"Content-Type":content_type, "Content-Disposition": f"inline; filename='{filename}'"},
+ "body": buffer.getvalue().decode()
+ }
+
+
+def process_path_param(ow_path: str):
+ """ Process the __ow_path parameters to extract username and fully qualified filename
+
+ :parma ow_path, the __ow_path parameter passed by openwhisk action controller
+ :return a dictionary {bucket:<bucket>, filename:<filepath>}
+ """
+ path_params = ow_path
+ if ow_path.startswith("/"):
+ path_params = ow_path[1:None]
+
+ path_elements = path_params.split("/")
+
+ upload_data = {}
+
+ if len(path_elements) >= 1:
+ upload_data['bucket']=path_elements[0]
+ upload_data['path']=path_params.replace(f"{upload_data['bucket']}/","")
+
+ file_elements = upload_data['path'].split("/")
+
+ if len(file_elements) > 1:
+ upload_data['filename']=file_elements[len(file_elements)-1]
+ else:
+ upload_data['filename']=file_elements
+
+ return upload_data
+
+
+def main(args):
+ """
+ Action implementing a generic download wrapper for the nuv devel plugin. The invoker must provide a x-impersonate-auth header containing the Openwhisk BASIC authentication of the wsku/user the action should impersonate
+ when calling this action. The upload action it is supposed to receive a path param similar to /<bucket>/<path>
+ and will attempt to retrieve the given path under the given MINIO <bucket>. The bucket must exists and the impersonated user must have read permission on it.
+ """
+ headers = args['__ow_headers']
+ if('x-impersonate-auth' not in headers):
+ return build_error("invalid request, missing mandatory header: x-impersonate-auth")
+
+ try:
+ download_data = process_path_param(args['__ow_path'])
+
+ if 'bucket' not in download_data and 'filename' not in download_data:
+ return build_error("invalid request, bucket and/or filename path error")
+
+ print(f"processing request to download {download_data['path']} from bucket {download_data['bucket']}")
+ user_data = Authorize(args['couchdb_host'],args['couchdb_user'],args['couchdb_password']).login(headers['x-impersonate-auth'])
+
+ mo_client = mutil.build_mo_client(ut.get_env_value(user_data,"MINIO_HOST"), ut.get_env_value(user_data,"MINIO_PORT"),ut.get_env_value(user_data,"MINIO_ACCESS_KEY") , ut.get_env_value(user_data,"MINIO_SECRET_KEY"))
+
+ # see https://urllib3.readthedocs.io/en/latest/reference/urllib3.response.html for the format
+ response = mo_client.get_object(bucket_name = download_data['bucket'], object_name= download_data['path'])
+
+ content_type = response.getheader('Content-Type')
+ buffer = io.BytesIO()
+
+ for d in response.stream():
+ buffer.write(d)
+
+ return build_response(download_data['filename'], content_type, buffer)
+ except Exception as e:
+ return build_error(f"failed to execute nuv devel command. Reason: {e}")
+ finally:
+ if response:
+ response.close()
+ response.release_conn()
diff --git a/actions/devel/download/common/authorize.py b/actions/devel/download/common/authorize.py
new file mode 100644
index 0000000..f7d716f
--- /dev/null
+++ b/actions/devel/download/common/authorize.py
@@ -0,0 +1,162 @@
+# 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.
+#
+from base64 import b64decode, b64encode
+from urllib.parse import quote, unquote
+
+import nuvolaris.config as cfg
+import nuvolaris.couchdb_util as cu
+import json
+import common.util as ut
+
+USER_META_DBN = "users_metadata"
+SUBJECT_META_DBN = "subjects"
+
+class DecodeError(Exception):
+ pass
+
+class EncodeError(Exception):
+ pass
+
+class AuthorizationError(Exception):
+ pass
+
+class Authorize():
+
+ def __init__(self, cdb_host, cdb_user, cdb_pwd):
+ cfg.clean()
+ cfg.put("couchdb.host", cdb_host)
+ cfg.put("couchdb.admin.user", cdb_user)
+ cfg.put("couchdb.admin.password", cdb_pwd)
+ self._db = cu.CouchDB()
+
+ def encode(self,username, password):
+ """Returns an HTTP basic authentication encrypted string given a valid
+ username and password.
+ """
+ if ':' in username:
+ raise EncodeError
+
+ username_password = f'{quote(username)}:{quote(password)}'
+ return f'Basic {b64encode(username_password.encode()).decode()}'
+
+ def _parse_b64(self, encoded_str):
+ try:
+ username, password = b64decode(encoded_str).decode().split(':', 1)
+ except:
+ # fallback in case the token is not bas64 encoded
+ username, password = encoded_str.split(':', 1)
+
+ if not username or not password:
+ raise DecodeError("authentication token does not seems to be b64 encoded")
+
+ return username, password
+
+ def decode(self, encoded_str):
+ """Decode an encrypted HTTP basic authentication string. Returns a tuple of
+ the form (username, password), and raises a DecodeError exception if
+ nothing could be decoded.
+ """
+ split = encoded_str.strip().split(' ')
+
+ # If split is only one element, try to decode the username and password
+ # directly.
+ if len(split) == 1:
+ username, password = self._parse_b64(split[0])
+
+ # If there are only two elements, check the first and ensure it says
+ # 'basic' so that we know we're about to decode the right thing. If not,
+ # bail out.
+ elif len(split) == 2:
+ if split[0].strip().lower() == 'basic':
+ username, password = self._parse_b64(split[1])
+ else:
+ raise DecodeError("authentication token provides more than 2 elements. could not parse properly")
+
+ # If there are more than 2 elements, something crazy must be happening.
+ # Bail.
+ else:
+ raise DecodeError("unpredictable error parsing authentication token")
+
+ return unquote(username), unquote(password)
+
+ def fetch_subject(self, uuid: str, key: str):
+ """
+ Query the internal couchdb searching for the subject mathing the given uuid, key.
+ Normally these stored in wsk or wsku in the form uuid:key
+ :param uuid the OW subject uuid
+ :param key the OW subject key
+ :return a ubject document
+ """
+ print(f"searching for openwhisk subject {uuid}")
+ try:
+ selector= {
+ "selector": {"namespaces": {"$elemMatch": {"uuid": uuid,"key": key}}}
+ }
+
+ response = self._db.find_doc(SUBJECT_META_DBN, json.dumps(selector))
+
+ if(response['docs']):
+ docs = list(response['docs'])
+ if(len(docs) > 0):
+ print(f"Nuvolaris namespace for user {uuid} found. Returning Result.")
+ return docs[0]
+
+ print(f"Nuvolaris metadata for user {uuid} not found!")
+ return None
+ except Exception as e:
+ print(f"failed to query Nuvolaris metadata for user {uuid}. Reason: {e}")
+ return None
+
+ def fetch_user_data(self, username: str):
+ """
+ Query the internal couchdb searching for the given principal to retrieve all the
+ relevant metadata
+ """
+ print(f"searching for user {username} meta-data")
+ try:
+ selector = {"selector":{"login": {"$eq": username }}}
+ response = self._db.find_doc(USER_META_DBN, json.dumps(selector))
+
+ if(response['docs']):
+ docs = list(response['docs'])
+ if(len(docs) > 0):
+ print(f"Nuvolaris metadata for user {username} found. Returning Result.")
+ return docs[0]
+
+ print(f"Nuvolaris metadata for user {username} not found!")
+ return None
+ except Exception as e:
+ print(f"failed to query Nuvolaris metadata for user {username}. Reason: {e}")
+ return None
+
+ def login(self, authorization: str):
+ """
+ Attempt to login the user identified by the given Openwhisk authorization AUTH token as base64
+ """
+ uuid, key = self.decode(authorization)
+ subject = self.fetch_subject(uuid, key)
+
+ if not subject:
+ raise AuthorizationError("Openwhisk subject not found.")
+
+ user_data = self.fetch_user_data(subject['subject'])
+
+ if user_data:
+ return user_data
+
+ raise AuthorizationError("Could not retrieve user metadata.")
\ No newline at end of file
diff --git a/actions/devel/download/common/command_data.py b/actions/devel/download/common/command_data.py
new file mode 100644
index 0000000..44f3faa
--- /dev/null
+++ b/actions/devel/download/common/command_data.py
@@ -0,0 +1,50 @@
+# 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 json
+import logging
+
+class CommandData:
+ _data = {}
+ _raw_data = {}
+
+ def __init__(self, cmd):
+ self._data = cmd
+ self._raw_data = cmd.copy()
+
+ def dump(self):
+ logging.debug(json.dumps(self._data))
+
+ def status(self, status):
+ self._data['status']=status
+
+ def result(self, result):
+ self._data['result']=result
+
+ def get_metadata(self):
+ return self._data
+
+ def get_raw_data(self):
+ return self._raw_data
+
+ def command(self):
+ return self._data['command']
+
+ def args(self):
+ return self._data['args']
+
+
diff --git a/actions/devel/download/common/minio_util.py b/actions/devel/download/common/minio_util.py
new file mode 100644
index 0000000..e1e8f90
--- /dev/null
+++ b/actions/devel/download/common/minio_util.py
@@ -0,0 +1,182 @@
+# 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 os
+import base64
+import random
+import string
+import mimetypes
+
+from minio import Minio
+from minio.error import S3Error
+from minio.commonconfig import CopySource
+
+def extract_mimetype(file):
+ mimetype, _ = mimetypes.guess_type(file)
+
+ if mimetype is None:
+ return "application/octet-stream"
+ else:
+ return mimetype
+
+def build_mo_client(host, port, access_key, secret_key):
+ """
+ Creates an Minio client pointing to the given MINIO HOST
+ :param host, minio host
+ :param port, minio api port normally it is the 9000
+ :param access_key user we are representing
+ :param secret_key to access minio
+ """
+ mo_client = Minio(f"{host}:{port}",access_key=access_key,secret_key=secret_key,secure=False)
+ return mo_client
+
+def upload_file(mo_client, file, bucket, object_name=None):
+ """Upload a file to an S3 bucket
+
+ :param file_name: File to upload
+ :param bucket: Bucket to upload to
+ :param object_name: S3 object name. If not specified then file_name is used
+ :return: a tuple with the Booleand str,
+ """
+
+ # If S3 object_name was not specified, use file_name
+ if object_name is None:
+ object_name = os.path.basename(file)
+
+ print(f"uploading {object_name} into bucket {bucket} from tmp_file {file}")
+
+ # Upload the file
+ try:
+ mimetype = extract_mimetype(object_name)
+ response = mo_client.fput_object(bucket_name=bucket,object_name=object_name,file_path=file,content_type=mimetype)
+ if response._object_name:
+ return True, response._object_name
+ except Exception as e:
+ print(e)
+ return False, str(e)
+ return False, "KO"
+
+def prepare_file_upload(username, filename, file_content_as_b64):
+ """ Creates a tmp area for the given user where the uploaded file will be stored under a random generated name
+ the fully qualified filename is taken into account only on the corresponding destination bucket.
+ param: username
+ param: filename
+ param: file_content_as_b64
+ return: a file object pointing to the tmp file
+ """
+ try:
+ user_tmp_folder = f"/tmp/{username}"
+ if not os.path.exists(user_tmp_folder):
+ os.makedirs(user_tmp_folder)
+ print(f"added tmp folder {user_tmp_folder}")
+
+ delete_files_in_directory_and_subdirectories(user_tmp_folder)
+ rnd_filename = get_random_string(20)
+ tmp_file = f"{user_tmp_folder}/{rnd_filename}"
+
+ with open(tmp_file, "wb") as f:
+ file_content=base64.b64decode(file_content_as_b64)
+ f.write(file_content)
+
+ if os.path.exists(tmp_file):
+ print(f"{tmp_file} stored successfully.")
+ return tmp_file
+ else:
+ return None
+ except Exception as e:
+ print("error preparing tmp_files",e)
+ return None
+
+def delete_files_in_directory_and_subdirectories(directory_path):
+ try:
+ for root, dirs, files in os.walk(directory_path):
+ for file in files:
+ file_path = os.path.join(root, file)
+ os.remove(file_path)
+ print("All files and subdirectories deleted successfully.")
+ except OSError:
+ print("Error occurred while deleting files and subdirectories.")
+
+def get_random_string(length):
+ # choose from all lowercase letter
+ letters = string.ascii_lowercase
+ return ''.join(random.choice(letters) for i in range(length))
+
+def rm_file(mo_client, bucket, file):
+ """ Remove a file from a bucket
+ :parma mo_client a minio client instance to execute the command
+ :param bucket the bucket name
+ :param file the file name
+ :return True if the file has been removed, False otherwise
+ """
+
+ try:
+ mo_client.remove_object(bucket, file)
+ return True
+ except Exception as e:
+ print(e)
+ return False
+ return False
+
+def cp_file(mo_client, orig_bucket, orig_file, dest_bucket, dest_file):
+ """Copy a file between two buckets
+
+ :param orig_bucket: Origin bucket
+ :param orig_file: Origin file
+ :param dest_bucket: Destination bucket
+ :param dest_file: Destination file
+ :return: name of the destination file is everything is ok. None otherwise
+ """
+ print(f"**** moving file {orig_file} from bucket {orig_bucket} to bucket {dest_bucket} with name {dest_file}")
+ try:
+ # copy an object from a bucket to another.
+ cp_result = mo_client.copy_object(
+ dest_bucket,
+ dest_file,
+ CopySource(orig_bucket, orig_file)
+ )
+ if cp_result.object_name:
+ return cp_result.object_name
+ except Exception as e:
+ print(e)
+ return None
+ return None
+
+def mv_file(mo_client, orig_bucket, orig_file, dest_bucket, dest_file):
+ """Move a file between two buckets
+
+ :param orig_bucket: Origin bucket
+ :param orig_file: Origin file
+ :param dest_bucket: Destination bucket
+ :param dest_file: Destination file
+ :return: name of the destination file is everything is ok. None otherwise
+ """
+ print(f"**** moving file {orig_file} from bucket {orig_bucket} to bucket {dest_bucket} with name {dest_file}")
+
+ # Upload the file
+ try:
+ # mv an object from a bucket to another.
+ object_name = cp_file(mo_client, orig_bucket, orig_file, dest_bucket, dest_file)
+ if object_name:
+ rm_file(mo_client,orig_bucket,orig_file)
+ return object_name
+ except Exception as e:
+ print(e)
+ return None
+ return None
+
diff --git a/actions/devel/download/common/util.py b/actions/devel/download/common/util.py
new file mode 100644
index 0000000..f1e8f39
--- /dev/null
+++ b/actions/devel/download/common/util.py
@@ -0,0 +1,35 @@
+# 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 mimetypes
+
+def get_env_value(user_data, key):
+ """
+ Check if inside the given user_data object there is an env item with the
+ given name and returns the value if any.
+ :param user_data
+ :param key
+ :return None if the given key it is not prese
+ """
+ envs = list(user_data['env'])
+
+ for env in envs:
+ if env['key'] == key:
+ return env['value']
+
+ return None
\ No newline at end of file
diff --git a/actions/devel/download/flatdict.py b/actions/devel/download/flatdict.py
new file mode 100644
index 0000000..834ce99
--- /dev/null
+++ b/actions/devel/download/flatdict.py
@@ -0,0 +1,506 @@
+# 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.
+
+"""FlatDict is a dict object that allows for single level, delimited
+key/value pair mapping of nested dictionaries.
+
+"""
+try:
+ from collections.abc import MutableMapping
+except ImportError: # pragma: nocover
+ from collections import MutableMapping
+import sys
+
+__version__ = '4.0.1'
+
+NO_DEFAULT = object()
+
+
+class FlatDict(MutableMapping):
+ """:class:`~flatdict.FlatDict` is a dictionary object that allows for
+ single level, delimited key/value pair mapping of nested dictionaries.
+ The default delimiter value is ``:`` but can be changed in the constructor
+ or by calling :meth:`FlatDict.set_delimiter`.
+
+ """
+ _COERCE = dict
+
+ def __init__(self, value=None, delimiter=':', dict_class=dict):
+ super(FlatDict, self).__init__()
+ self._values = dict_class()
+ self._delimiter = delimiter
+ self.update(value)
+
+ def __contains__(self, key):
+ """Check to see if the key exists, checking for both delimited and
+ not delimited key values.
+
+ :param mixed key: The key to check for
+
+ """
+ if self._has_delimiter(key):
+ pk, ck = key.split(self._delimiter, 1)
+ return pk in self._values and ck in self._values[pk]
+ return key in self._values
+
+ def __delitem__(self, key):
+ """Delete the item for the specified key, automatically dealing with
+ nested children.
+
+ :param mixed key: The key to use
+ :raises: KeyError
+
+ """
+ if key not in self:
+ raise KeyError
+ if self._has_delimiter(key):
+ pk, ck = key.split(self._delimiter, 1)
+ del self._values[pk][ck]
+ if not self._values[pk]:
+ del self._values[pk]
+ else:
+ del self._values[key]
+
+ def __eq__(self, other):
+ """Check for equality against the other value
+
+ :param other: The value to compare
+ :type other: FlatDict
+ :rtype: bool
+ :raises: TypeError
+
+ """
+ if isinstance(other, dict):
+ return self.as_dict() == other
+ elif not isinstance(other, self.__class__):
+ raise TypeError
+ return self.as_dict() == other.as_dict()
+
+ def __ne__(self, other):
+ """Check for inequality against the other value
+
+ :param other: The value to compare
+ :type other: dict or FlatDict
+ :rtype: bool
+
+ """
+ return not self.__eq__(other)
+
+ def __getitem__(self, key):
+ """Get an item for the specified key, automatically dealing with
+ nested children.
+
+ :param mixed key: The key to use
+ :rtype: mixed
+ :raises: KeyError
+
+ """
+ values = self._values
+ key = [key] if isinstance(key, int) else key.split(self._delimiter)
+ for part in key:
+ values = values[part]
+ return values
+
+ def __iter__(self):
+ """Iterate over the flat dictionary key and values
+
+ :rtype: Iterator
+ :raises: RuntimeError
+
+ """
+ return iter(self.keys())
+
+ def __len__(self):
+ """Return the number of items.
+
+ :rtype: int
+
+ """
+ return len(self.keys())
+
+ def __reduce__(self):
+ """Return state information for pickling
+
+ :rtype: tuple
+
+ """
+ return type(self), (self.as_dict(), self._delimiter)
+
+ def __repr__(self):
+ """Return the string representation of the instance.
+
+ :rtype: str
+
+ """
+ return '<{} id={} {}>"'.format(self.__class__.__name__, id(self),
+ str(self))
+
+ def __setitem__(self, key, value):
+ """Assign the value to the key, dynamically building nested
+ FlatDict items where appropriate.
+
+ :param mixed key: The key for the item
+ :param mixed value: The value for the item
+ :raises: TypeError
+
+ """
+ if isinstance(value, self._COERCE) and not isinstance(value, FlatDict):
+ value = self.__class__(value, self._delimiter)
+ if self._has_delimiter(key):
+ pk, ck = key.split(self._delimiter, 1)
+ if pk not in self._values:
+ self._values[pk] = self.__class__({ck: value}, self._delimiter)
+ return
+ elif not isinstance(self._values[pk], FlatDict):
+ raise TypeError(
+ 'Assignment to invalid type for key {}'.format(pk))
+ self._values[pk][ck] = value
+ else:
+ self._values[key] = value
+
+ def __str__(self):
+ """Return the string value of the instance.
+
+ :rtype: str
+
+ """
+ return '{{{}}}'.format(', '.join(
+ ['{!r}: {!r}'.format(k, self[k]) for k in self.keys()]))
+
+ def as_dict(self):
+ """Return the :class:`~flatdict.FlatDict` as a :class:`dict`
+
+ :rtype: dict
+
+ """
+ out = dict({})
+ for key in self.keys():
+ if self._has_delimiter(key):
+ pk, ck = key.split(self._delimiter, 1)
+ if self._has_delimiter(ck):
+ ck = ck.split(self._delimiter, 1)[0]
+ if isinstance(self._values[pk], FlatDict) and pk not in out:
+ out[pk] = {}
+ if isinstance(self._values[pk][ck], FlatDict):
+ out[pk][ck] = self._values[pk][ck].as_dict()
+ else:
+ out[pk][ck] = self._values[pk][ck]
+ else:
+ out[key] = self._values[key]
+ return out
+
+ def clear(self):
+ """Remove all items from the flat dictionary."""
+ self._values.clear()
+
+ def copy(self):
+ """Return a shallow copy of the flat dictionary.
+
+ :rtype: flatdict.FlatDict
+
+ """
+ return self.__class__(self.as_dict(), delimiter=self._delimiter)
+
+ def get(self, key, d=None):
+ """Return the value for key if key is in the flat dictionary, else
+ default. If default is not given, it defaults to ``None``, so that this
+ method never raises :exc:`KeyError`.
+
+ :param mixed key: The key to get
+ :param mixed d: The default value
+ :rtype: mixed
+
+ """
+ try:
+ return self.__getitem__(key)
+ except KeyError:
+ return d
+
+ def items(self):
+ """Return a copy of the flat dictionary's list of ``(key, value)``
+ pairs.
+
+ .. note:: CPython implementation detail: Keys and values are listed in
+ an arbitrary order which is non-random, varies across Python
+ implementations, and depends on the flat dictionary's history of
+ insertions and deletions.
+
+ :rtype: list
+
+ """
+ return [(k, self.__getitem__(k)) for k in self.keys()]
+
+ def iteritems(self):
+ """Return an iterator over the flat dictionary's (key, value) pairs.
+ See the note for :meth:`flatdict.FlatDict.items`.
+
+ Using ``iteritems()`` while adding or deleting entries in the flat
+ dictionary may raise :exc:`RuntimeError` or fail to iterate over all
+ entries.
+
+ :rtype: Iterator
+ :raises: RuntimeError
+
+ """
+ for item in self.items():
+ yield item
+
+ def iterkeys(self):
+ """Iterate over the flat dictionary's keys. See the note for
+ :meth:`flatdict.FlatDict.items`.
+
+ Using ``iterkeys()`` while adding or deleting entries in the flat
+ dictionary may raise :exc:`RuntimeError` or fail to iterate over all
+ entries.
+
+ :rtype: Iterator
+ :raises: RuntimeError
+
+ """
+ for key in self.keys():
+ yield key
+
+ def itervalues(self):
+ """Return an iterator over the flat dictionary's values. See the note
+ :meth:`flatdict.FlatDict.items`.
+
+ Using ``itervalues()`` while adding or deleting entries in the flat
+ dictionary may raise a :exc:`RuntimeError` or fail to iterate over all
+ entries.
+
+ :rtype: Iterator
+ :raises: RuntimeError
+
+ """
+ for value in self.values():
+ yield value
+
+ def keys(self):
+ """Return a copy of the flat dictionary's list of keys.
+ See the note for :meth:`flatdict.FlatDict.items`.
+
+ :rtype: list
+
+ """
+ keys = []
+
+ for key, value in self._values.items():
+ if isinstance(value, (FlatDict, dict)):
+ nested = [
+ self._delimiter.join([str(key), str(k)])
+ for k in value.keys()]
+ keys += nested if nested else [key]
+ else:
+ keys.append(key)
+
+ return keys
+
+ def pop(self, key, default=NO_DEFAULT):
+ """If key is in the flat dictionary, remove it and return its value,
+ else return default. If default is not given and key is not in the
+ dictionary, :exc:`KeyError` is raised.
+
+ :param mixed key: The key name
+ :param mixed default: The default value
+ :rtype: mixed
+
+ """
+ if key not in self and default != NO_DEFAULT:
+ return default
+ value = self[key]
+ self.__delitem__(key)
+ return value
+
+ def setdefault(self, key, default):
+ """If key is in the flat dictionary, return its value. If not,
+ insert key with a value of default and return default.
+ default defaults to ``None``.
+
+ :param mixed key: The key name
+ :param mixed default: The default value
+ :rtype: mixed
+
+ """
+ if key not in self:
+ self.__setitem__(key, default)
+ return self.__getitem__(key)
+
+ def set_delimiter(self, delimiter):
+ """Override the default or passed in delimiter with a new value. If
+ the requested delimiter already exists in a key, a :exc:`ValueError`
+ will be raised.
+
+ :param str delimiter: The delimiter to use
+ :raises: ValueError
+
+ """
+ for key in self.keys():
+ if delimiter in key:
+ raise ValueError('Key {!r} collides with delimiter {!r}', key,
+ delimiter)
+ self._delimiter = delimiter
+ for key in self._values.keys():
+ if isinstance(self._values[key], FlatDict):
+ self._values[key].set_delimiter(delimiter)
+
+ def update(self, other=None, **kwargs):
+ """Update the flat dictionary with the key/value pairs from other,
+ overwriting existing keys.
+
+ ``update()`` accepts either another flat dictionary object or an
+ iterable of key/value pairs (as tuples or other iterables of length
+ two). If keyword arguments are specified, the flat dictionary is then
+ updated with those key/value pairs: ``d.update(red=1, blue=2)``.
+
+ :param iterable other: Iterable of key, value pairs
+ :rtype: None
+
+ """
+ [self.__setitem__(k, v) for k, v in dict(other or kwargs).items()]
+
+ def values(self):
+ """Return a copy of the flat dictionary's list of values. See the note
+ for :meth:`flatdict.FlatDict.items`.
+
+ :rtype: list
+
+ """
+ return [self.__getitem__(k) for k in self.keys()]
+
+ def _has_delimiter(self, key):
+ """Checks to see if the key contains the delimiter.
+
+ :rtype: bool
+
+ """
+ return isinstance(key, str) and self._delimiter in key
+
+
+class FlatterDict(FlatDict):
+ """Like :class:`~flatdict.FlatDict` but also coerces lists and sets
+ to child-dict instances with the offset as the key. Alternative to
+ the implementation added in v1.2 of FlatDict.
+
+ """
+ _COERCE = list, tuple, set, dict, FlatDict
+ _ARRAYS = list, set, tuple
+
+ def __init__(self, value=None, delimiter=':', dict_class=dict):
+ self.original_type = type(value)
+ if self.original_type in self._ARRAYS:
+ value = {str(i): v for i, v in enumerate(value)}
+ super(FlatterDict, self).__init__(value, delimiter, dict_class)
+
+ def __setitem__(self, key, value):
+ """Assign the value to the key, dynamically building nested
+ FlatDict items where appropriate.
+
+ :param mixed key: The key for the item
+ :param mixed value: The value for the item
+ :raises: TypeError
+
+ """
+ if isinstance(value, self._COERCE) and \
+ not isinstance(value, FlatterDict):
+ value = self.__class__(value, self._delimiter)
+ if self._has_delimiter(key):
+ pk, ck = key.split(self._delimiter, 1)
+ if pk not in self._values:
+ self._values[pk] = self.__class__({ck: value}, self._delimiter)
+ return
+ if getattr(self._values[pk], 'original_type',
+ None) in self._ARRAYS:
+ try:
+ k, cck = ck.split(self._delimiter, 1)
+ int(k)
+ except ValueError:
+ raise TypeError(
+ 'Assignment to invalid type for key {}{}{}'.format(
+ pk, self._delimiter, ck))
+ self._values[pk][k][cck] = value
+ return
+ elif not isinstance(self._values[pk], FlatterDict):
+ raise TypeError(
+ 'Assignment to invalid type for key {}'.format(pk))
+ self._values[pk][ck] = value
+ else:
+ self._values[key] = value
+
+ def as_dict(self):
+ """Return the :class:`~flatdict.FlatterDict` as a nested
+ :class:`dict`.
+
+ :rtype: dict
+
+ """
+ out = {}
+ for key in self.keys():
+ if self._has_delimiter(key):
+ pk, ck = key.split(self._delimiter, 1)
+ if self._has_delimiter(ck):
+ ck = ck.split(self._delimiter, 1)[0]
+ if isinstance(self._values[pk], FlatterDict) and pk not in out:
+ if self._values[pk].original_type == tuple:
+ out[pk] = tuple(self._child_as_list(pk))
+ elif self._values[pk].original_type == list:
+ out[pk] = self._child_as_list(pk)
+ elif self._values[pk].original_type == set:
+ out[pk] = set(self._child_as_list(pk))
+ elif self._values[pk].original_type == dict:
+ out[pk] = self._values[pk].as_dict()
+ else:
+ if isinstance(self._values[key], FlatterDict):
+ out[key] = self._values[key].original_type()
+ else:
+ out[key] = self._values[key]
+ return out
+
+ def _child_as_list(self, pk, ck=None):
+ """Returns a list of values from the child FlatterDict instance
+ with string based integer keys.
+
+ :param str pk: The parent key
+ :param str ck: The child key, optional
+ :rtype: list
+
+ """
+ if ck is None:
+ subset = self._values[pk]
+ else:
+ subset = self._values[pk][ck]
+ # Check if keys has delimiter, which implies deeply nested dict
+ keys = subset.keys()
+ if any(self._has_delimiter(k) for k in keys):
+ out = []
+ split_keys = {k.split(self._delimiter)[0] for k in keys}
+ for k in sorted(split_keys, key=lambda x: int(x)):
+ if subset[k].original_type == tuple:
+ out.append(tuple(self._child_as_list(pk, k)))
+ elif subset[k].original_type == list:
+ out.append(self._child_as_list(pk, k))
+ elif subset[k].original_type == set:
+ out.append(set(self._child_as_list(pk, k)))
+ elif subset[k].original_type == dict:
+ out.append(subset[k].as_dict())
+ return out
+
+ # Python prior 3.6 does not guarantee insertion order, remove it after
+ # EOL python 3.5 - 2020-09-13
+ if sys.version_info[0:2] < (3, 6): # pragma: nocover
+ return [subset[k] for k in sorted(keys, key=lambda x: int(x))]
+ else:
+ return [subset[k] for k in keys]
diff --git a/actions/devel/download/nuvolaris/config.py b/actions/devel/download/nuvolaris/config.py
new file mode 100644
index 0000000..787d4a7
--- /dev/null
+++ b/actions/devel/download/nuvolaris/config.py
@@ -0,0 +1,202 @@
+# 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 flatdict, json, os
+import logging
+
+_config = {}
+
+# define a configuration
+# the configuration is a map, followed by a list of labels
+# the map can be a serialized json and will be flattened to a map of values.
+# you can have only a configuration active at a time
+# if you want to set a new configuration you have to clean it
+def configure(spec: dict):
+ global _config
+ _config = dict(flatdict.FlatDict(spec, delimiter="."))
+
+ #forces autodetect of kube if not provided
+ if not exists('nuvolaris.kube'):
+ put('nuvolaris.kube','auto')
+
+ return True
+
+def clean():
+ global _config
+ _config = {}
+
+def exists(key):
+ return key in _config
+
+def get(key, envvar=None, defval=None):
+ val = _config.get(key)
+
+ if envvar and envvar in os.environ:
+ val = os.environ[envvar]
+
+ if val:
+ return val
+
+ return defval
+
+def put(key, value):
+ _config[key] = value
+ return True
+
+def delete(key):
+ if key in _config:
+ del _config[key]
+ return True
+
+def getall(prefix=""):
+ res = {}
+ for key in _config.keys():
+ if key.startswith(prefix):
+ res[key] = _config[key]
+ return res
+
+def keys(prefix=""):
+ res = []
+ if _config:
+ for key in _config.keys():
+ if key.startswith(prefix):
+ res.append(key)
+ return res
+
+def detect_labels(labels=None):
+ # skip autodetection of nuvolaris.kube if already available in _config
+ if exists('nuvolaris.kube') and get('nuvolaris.kube') != 'auto':
+ logging.info(f"*** configuration provided already a nuvolaris.kube={get('nuvolaris.kube')}")
+ return {}
+
+ # read labels if not available
+ if not labels:
+ import nuvolaris.kube as kube
+ labels = kube.kubectl("get", "nodes", jsonpath='{.items[].metadata.labels}')
+
+ res = {}
+ kube = None
+ for i in labels:
+ for j in list(i.keys()):
+ if j.startswith("nuvolaris.io/"):
+ key = f"nuvolaris.{j[13:]}"
+ res[key] = i[j]
+ _config[key] = i[j]
+
+ for i in labels:
+ for j in list(i.keys()):
+ # detect the kube type
+ if j.find("eksctl.io") >= 0:
+ kube ="eks"
+ break
+ if j.find("k8s.io/cloud-provider-aws") >= 0:
+ kube ="eks"
+ break
+ elif j.find("microk8s.io") >= 0:
+ kube = "microk8s"
+ break
+ elif j.find("lke.linode.com") >=0:
+ kube = "lks"
+ break
+ elif j.find("node.openshift.io") >=0:
+ kube = "openshift"
+ break
+ elif j.endswith("kubernetes.io/instance-type") and i[j] == "k3s":
+ kube = "k3s"
+ break
+ elif j.find("cloud.google.com/gke") >=0:
+ kube = "gke"
+ break
+ elif j.find("kubernetes.azure.com") >=0:
+ kube = "aks"
+ break
+ # assign all the 'nuvolaris.io' labels
+
+ if kube:
+ res["nuvolaris.kube"] = kube
+ _config["nuvolaris.kube"] = kube
+ return res
+
+ # defaults to generic if it is not yet detected
+ if exists('nuvolaris.kube') and get('nuvolaris.kube') == 'auto':
+ _config["nuvolaris.kube"] = "generic"
+ res["nuvolaris.kube"] = "generic"
+
+ return res
+
+def detect_storage(storages=None):
+ res = {}
+ if not storages:
+ import nuvolaris.util as util
+
+ try:
+ detect_storageclass = True
+ detect_provisioner = True
+
+ # skips autodetection if already provided
+ if exists('nuvolaris.storageclass') and get('nuvolaris.storageclass') != 'auto':
+ logging.info(f"*** configuration provided already a nuvolaris.storageclass={get('nuvolaris.storageclass')}")
+ detect_storageclass = False
+
+ if exists('nuvolaris.provisioner') and get('nuvolaris.provisioner') != 'auto':
+ logging.info(f"*** configuration provided already a nuvolaris.provisioner={get('nuvolaris.provisioner')}")
+ detect_provisioner = False
+
+ if not detect_storageclass and not detect_provisioner:
+ return res
+
+ if detect_storageclass:
+ storage_class = util.get_default_storage_class()
+ if storage_class:
+ res['nuvolaris.storageclass'] = storage_class
+ _config['nuvolaris.storageclass'] = storage_class
+
+ if detect_provisioner:
+ provisioner = util.get_default_storage_provisioner()
+ if(provisioner):
+ res['nuvolaris.provisioner'] = provisioner
+ _config['nuvolaris.provisioner'] = provisioner
+ except:
+ pass
+ return res
+
+def detect_env():
+ _config['operator.image'] = os.environ.get("OPERATOR_IMAGE", "missing-OPERATOR_IMAGE")
+ _config['operator.tag'] = os.environ.get("OPERATOR_TAG", "missing-OPERATOR_TAG")
+
+ # skip autodetection of controller.image if already configure into whisk.yaml
+ if not exists('controller.image'):
+ _config['controller.image'] = os.environ.get("CONTROLLER_IMAGE", "missing-CONTROLLER_IMAGE")
+ _config['controller.tag'] = os.environ.get("CONTROLLER_TAG", "missing-CONTROLLER_TAG")
+ else:
+ logging.warn(f"OW controller image detection skipped. Using {get('controller.image')}")
+
+ if not exists('invoker.image'):
+ _config['invoker.image'] = os.environ.get("INVOKER_IMAGE", "missing-INVOKER_IMAGE")
+ _config['invoker.tag'] = os.environ.get("INVOKER_TAG", "missing-INVOKER_TAG")
+ else:
+ logging.warn(f"OW invoker image detection skipped. Using {get('invoker.image')}")
+
+def detect():
+ detect_storage()
+ detect_labels()
+ detect_env()
+
+def dump_config():
+ import nuvolaris.config as cfg
+ for k in cfg.getall():
+ logging.debug(f"{k} = {cfg.get(k)}")
diff --git a/actions/devel/download/nuvolaris/couchdb_util.py b/actions/devel/download/nuvolaris/couchdb_util.py
new file mode 100644
index 0000000..3adb6b9
--- /dev/null
+++ b/actions/devel/download/nuvolaris/couchdb_util.py
@@ -0,0 +1,164 @@
+# 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 os, json, time, sys, logging
+import requests as req
+import nuvolaris.config as cfg
+
+class CouchDB:
+ def __init__(self):
+ self.db_protocol = "http"
+ self.db_prefix = "nuvolaris_"
+ self.db_port = cfg.get("couchdb.port", "COUCHDB_SERVICE_PORT", "5984")
+ self.db_host = cfg.get("couchdb.host", "COUCHDB_SERVICE_HOST", "couchdb")
+ self.db_username = cfg.get("couchdb.admin.user", "COUCHDB_ADMIN_USER", "whisk_admin")
+ self.db_password = cfg.get("couchdb.admin.password", "COUCHDB_ADMIN_PASSWORD", "some_passw0rd")
+ self.db_auth = req.auth.HTTPBasicAuth(self.db_username,self.db_password)
+ self.db_url = f"{self.db_protocol}://{self.db_host}:{self.db_port}"
+ self.db_base = f"{self.db_url}/{self.db_prefix}"
+ self.db_session = req.Session()
+ self.db_session.auth = self.db_auth
+
+ def wait_db_ready(self, max_seconds):
+ logging.info("entering CouchDB.wait_db_ready()")
+ start = time.time()
+ delta = 0
+ session = req.Session()
+ while delta < max_seconds:
+ try:
+ 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
+ except Exception as e:
+ logging.info(f"waiting since: {delta} seconds")
+ delta = int(time.time() - start)
+ time.sleep(1)
+ return False
+
+ # check if database exists, return boolean
+ def check_db(self, database):
+ url = f"{self.db_base}{database}"
+ r = self.db_session.head(url)
+ return r.status_code == 200
+
+ # delete database, return true if ok
+ def delete_db(self, database):
+ url = f"{self.db_base}{database}"
+ r = self.db_session.delete(url)
+ return r.status_code == 200
+
+ # create db, return true if ok
+ def create_db(self, database):
+ url = f"{self.db_base}{database}"
+ r = self.db_session.put(url)
+ return r.status_code == 201
+
+ # database="subjects"
+ def recreate_db(self, database, recreate=False):
+ msg = "recreate_db:"
+ exists = self.check_db(database)
+ if recreate and exists:
+ msg += " deleted"
+ self.delete_db(database)
+ if recreate or not exists:
+ msg += " created"
+ self.create_db(database)
+ return msg
+
+ def get_doc(self, database, id, user=None, password="", no_auth=False):
+ url = f"{self.db_base}{database}/{id}"
+ session = req.Session()
+ if no_auth:
+ db_auth=None
+ elif user:
+ db_auth=req.auth.HTTPBasicAuth(user, password)
+ else:
+ db_auth = self.db_auth
+ session.auth = db_auth
+ r = session.get(url)
+ if r.status_code == 200:
+ return json.loads(r.text)
+ return None
+
+ def update_doc(self, database, doc):
+ if '_id' in doc:
+ url = f"{self.db_base}{database}/{doc['_id']}"
+ cur = self.get_doc(database, doc['_id'])
+ if cur and '_rev' in cur:
+ doc['_rev'] = cur['_rev']
+ r = self.db_session.put(url, json=doc)
+ else:
+ r = self.db_session.put(url, json=doc)
+ return r.status_code in [200,201]
+ return False
+
+ def delete_doc(self, database, id):
+ cur = self.get_doc(database, id)
+ if cur and '_rev' in cur:
+ url = f"{self.db_base}{database}/{cur['_id']}?rev={cur['_rev']}"
+ r = self.db_session.delete(url)
+ return r.status_code == 200
+ return False
+
+ def configure_single_node(self):
+ url = f"{self.db_url}/_cluster_setup"
+ data = {"action": "enable_single_node", "singlenode": True, "bind_address": "0.0.0.0", "port": 5984}
+ r = self.db_session.post(url, json=data)
+ return r.status_code == 201
+
+ def configure_no_reduce_limit(self):
+ url = f"{self.db_url}/_node/_local/_config/query_server_config/reduce_limit"
+ data=b'"false"'
+ r = self.db_session.put(url, data=data)
+ return r.status_code == 200
+
+ def add_user(self, username: str, password: str):
+ userpass = {"name": username, "password": password, "roles": [], "type": "user"}
+ url = f"{self.db_url}/_users/org.couchdb.user:{username}"
+ res = self.db_session.put(url, json=userpass)
+ return res.status_code in [200, 201, 421]
+
+ #def add_role(self, database: str, members: list[str] = [], admins: list[str] =[]):
+ def add_role(self, database: str, members = [], admins =[]):
+ roles = {"admins": { "names": admins, "roles": [] }, "members": { "names": members, "roles": [] } }
+ url = f"{self.db_base}{database}/_security"
+ res = self.db_session.put(url, json=roles)
+ return res.status_code in [200, 201, 421]
+
+#
+# Submit a POST request to the _find endpoint using the specified selector
+#
+ def find_doc(self, database, selector, user=None, password="", no_auth=False):
+ url = f"{self.db_base}{database}/_find"
+ headers = {'Content-Type': 'application/json'}
+ session = req.Session()
+
+ if no_auth:
+ db_auth=None
+ elif user:
+ db_auth=req.auth.HTTPBasicAuth(user, password)
+ else:
+ db_auth = self.db_auth
+
+ session.auth=db_auth
+ r = session.post(url, headers=headers, data=selector)
+ if r.status_code == 200:
+ return json.loads(r.text)
+
+ logging.warn(f"query to {url} failed with {r.status_code}. Body {r.text}")
+ return None
\ No newline at end of file
diff --git a/actions/devel/ferretdb/__main__.py b/actions/devel/ferretdb/__main__.py
new file mode 100644
index 0000000..c741af8
--- /dev/null
+++ b/actions/devel/ferretdb/__main__.py
@@ -0,0 +1,73 @@
+# 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 json
+
+from common.authorize import Authorize
+from common.command_data import CommandData
+from command.ferretbd import FerretDB
+from base64 import b64decode
+
+class ApiError(Exception):
+ pass
+
+def build_error(message: str):
+ return {
+ "statusCode": 400,
+ "body": message
+ }
+
+def build_response(data:CommandData):
+ meta_data = data.get_metadata()
+ return {
+ "statusCode": meta_data['status'],
+ "body": meta_data['result']
+ }
+
+def parse_body(args):
+ try:
+ return b64decode(args['__ow_body']).decode().strip()
+ except Exception as e:
+ print(e)
+ raise ApiError("could not parse __ow_body as base64")
+
+def main(args):
+ """
+ Action implementing a generic command wrapper for the nuv devel mdb/ferretdb plugin. The action must be called with a POST request receiving a JSON
+ payload similar to this one
+ {
+ "command":"SET nuvolaris:key1 donald_duck"
+ }
+
+ the invoker must provide a x-impersonate-auth header containing the Openwhisk BASIC authentication of the wsku/user the action should impersonate
+ when submitting the command. Every specific provider will support specific request type and command.
+ WARNING: the body will be received as base64 encoded string as this action will be deployed with --web raw enabled flag
+ """
+ print(args)
+ headers = args['__ow_headers']
+ if('x-impersonate-auth' not in headers):
+ return build_error("invalid request, missing mandatory header: x-impersonate-auth")
+
+ if(len(args['__ow_body']) == 0):
+ return build_error("invalid request, no command payload received")
+
+ try:
+ user_data = Authorize(args['couchdb_host'],args['couchdb_user'],args['couchdb_password']).login(headers['x-impersonate-auth'])
+ cmd = CommandData(json.loads(parse_body(args)))
+ return build_response(FerretDB(user_data).execute(cmd))
+ except Exception as e:
+ return build_error(f"failed to execute nuv devel ferretdb. Reason: {str(e)}")
\ No newline at end of file
diff --git a/actions/devel/ferretdb/command/ferretbd.py b/actions/devel/ferretdb/command/ferretbd.py
new file mode 100644
index 0000000..42dbc0e
--- /dev/null
+++ b/actions/devel/ferretdb/command/ferretbd.py
@@ -0,0 +1,75 @@
+# 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 common.util as ut
+import json
+
+
+from pymongo import MongoClient
+from common.command_data import CommandData
+
+class FerretDB():
+ """
+ Implementation of a FerretDB/Mongodb Command executor. It will require
+ a user_data dictionary linked to a specific user.
+ """
+
+ def __init__(self, user_data):
+ self._user_data = user_data
+ self._mdb_url = ut.get_env_value(user_data,"MONGODB_URL")
+ self._db = user_data['login']
+ self.validate()
+
+ def validate(self):
+ """
+ Validate that the provided user_data contains the appropriate
+ metadata for being able to submit a redis command.
+ """
+ if not self._mdb_url or not self._db:
+ raise Exception("user does not have valid FERRETDB/MONGODB environment set")
+
+ def _get_db(self):
+ """
+ Get a reference to the connected user database
+ """
+ client = MongoClient(self._mdb_url)
+ return client[self._db]
+
+
+ def _send_command(self,input:CommandData):
+ print(f"inside ferretdb _send_command {json.dumps(input.get_raw_data())}")
+ db = self._get_db()
+ response = db.command(input.get_raw_data())
+
+ if response:
+ input.result(str(response))
+ input.status(200)
+ else:
+ input.result(f"ferretdb/mongodb operation failed")
+ input.status(400)
+
+ def execute(self, input:CommandData):
+ try:
+ input.status(400)
+ self._send_command(input)
+ except Exception as e:
+ print(e)
+ input.result(f"could not execute ferretbd command {e}")
+ input.status(400)
+
+ return input
\ No newline at end of file
diff --git a/actions/devel/ferretdb/common/authorize.py b/actions/devel/ferretdb/common/authorize.py
new file mode 100644
index 0000000..f7d716f
--- /dev/null
+++ b/actions/devel/ferretdb/common/authorize.py
@@ -0,0 +1,162 @@
+# 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.
+#
+from base64 import b64decode, b64encode
+from urllib.parse import quote, unquote
+
+import nuvolaris.config as cfg
+import nuvolaris.couchdb_util as cu
+import json
+import common.util as ut
+
+USER_META_DBN = "users_metadata"
+SUBJECT_META_DBN = "subjects"
+
+class DecodeError(Exception):
+ pass
+
+class EncodeError(Exception):
+ pass
+
+class AuthorizationError(Exception):
+ pass
+
+class Authorize():
+
+ def __init__(self, cdb_host, cdb_user, cdb_pwd):
+ cfg.clean()
+ cfg.put("couchdb.host", cdb_host)
+ cfg.put("couchdb.admin.user", cdb_user)
+ cfg.put("couchdb.admin.password", cdb_pwd)
+ self._db = cu.CouchDB()
+
+ def encode(self,username, password):
+ """Returns an HTTP basic authentication encrypted string given a valid
+ username and password.
+ """
+ if ':' in username:
+ raise EncodeError
+
+ username_password = f'{quote(username)}:{quote(password)}'
+ return f'Basic {b64encode(username_password.encode()).decode()}'
+
+ def _parse_b64(self, encoded_str):
+ try:
+ username, password = b64decode(encoded_str).decode().split(':', 1)
+ except:
+ # fallback in case the token is not bas64 encoded
+ username, password = encoded_str.split(':', 1)
+
+ if not username or not password:
+ raise DecodeError("authentication token does not seems to be b64 encoded")
+
+ return username, password
+
+ def decode(self, encoded_str):
+ """Decode an encrypted HTTP basic authentication string. Returns a tuple of
+ the form (username, password), and raises a DecodeError exception if
+ nothing could be decoded.
+ """
+ split = encoded_str.strip().split(' ')
+
+ # If split is only one element, try to decode the username and password
+ # directly.
+ if len(split) == 1:
+ username, password = self._parse_b64(split[0])
+
+ # If there are only two elements, check the first and ensure it says
+ # 'basic' so that we know we're about to decode the right thing. If not,
+ # bail out.
+ elif len(split) == 2:
+ if split[0].strip().lower() == 'basic':
+ username, password = self._parse_b64(split[1])
+ else:
+ raise DecodeError("authentication token provides more than 2 elements. could not parse properly")
+
+ # If there are more than 2 elements, something crazy must be happening.
+ # Bail.
+ else:
+ raise DecodeError("unpredictable error parsing authentication token")
+
+ return unquote(username), unquote(password)
+
+ def fetch_subject(self, uuid: str, key: str):
+ """
+ Query the internal couchdb searching for the subject mathing the given uuid, key.
+ Normally these stored in wsk or wsku in the form uuid:key
+ :param uuid the OW subject uuid
+ :param key the OW subject key
+ :return a ubject document
+ """
+ print(f"searching for openwhisk subject {uuid}")
+ try:
+ selector= {
+ "selector": {"namespaces": {"$elemMatch": {"uuid": uuid,"key": key}}}
+ }
+
+ response = self._db.find_doc(SUBJECT_META_DBN, json.dumps(selector))
+
+ if(response['docs']):
+ docs = list(response['docs'])
+ if(len(docs) > 0):
+ print(f"Nuvolaris namespace for user {uuid} found. Returning Result.")
+ return docs[0]
+
+ print(f"Nuvolaris metadata for user {uuid} not found!")
+ return None
+ except Exception as e:
+ print(f"failed to query Nuvolaris metadata for user {uuid}. Reason: {e}")
+ return None
+
+ def fetch_user_data(self, username: str):
+ """
+ Query the internal couchdb searching for the given principal to retrieve all the
+ relevant metadata
+ """
+ print(f"searching for user {username} meta-data")
+ try:
+ selector = {"selector":{"login": {"$eq": username }}}
+ response = self._db.find_doc(USER_META_DBN, json.dumps(selector))
+
+ if(response['docs']):
+ docs = list(response['docs'])
+ if(len(docs) > 0):
+ print(f"Nuvolaris metadata for user {username} found. Returning Result.")
+ return docs[0]
+
+ print(f"Nuvolaris metadata for user {username} not found!")
+ return None
+ except Exception as e:
+ print(f"failed to query Nuvolaris metadata for user {username}. Reason: {e}")
+ return None
+
+ def login(self, authorization: str):
+ """
+ Attempt to login the user identified by the given Openwhisk authorization AUTH token as base64
+ """
+ uuid, key = self.decode(authorization)
+ subject = self.fetch_subject(uuid, key)
+
+ if not subject:
+ raise AuthorizationError("Openwhisk subject not found.")
+
+ user_data = self.fetch_user_data(subject['subject'])
+
+ if user_data:
+ return user_data
+
+ raise AuthorizationError("Could not retrieve user metadata.")
\ No newline at end of file
diff --git a/actions/devel/ferretdb/common/command_data.py b/actions/devel/ferretdb/common/command_data.py
new file mode 100644
index 0000000..44f3faa
--- /dev/null
+++ b/actions/devel/ferretdb/common/command_data.py
@@ -0,0 +1,50 @@
+# 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 json
+import logging
+
+class CommandData:
+ _data = {}
+ _raw_data = {}
+
+ def __init__(self, cmd):
+ self._data = cmd
+ self._raw_data = cmd.copy()
+
+ def dump(self):
+ logging.debug(json.dumps(self._data))
+
+ def status(self, status):
+ self._data['status']=status
+
+ def result(self, result):
+ self._data['result']=result
+
+ def get_metadata(self):
+ return self._data
+
+ def get_raw_data(self):
+ return self._raw_data
+
+ def command(self):
+ return self._data['command']
+
+ def args(self):
+ return self._data['args']
+
+
diff --git a/actions/devel/ferretdb/common/minio_util.py b/actions/devel/ferretdb/common/minio_util.py
new file mode 100644
index 0000000..e1e8f90
--- /dev/null
+++ b/actions/devel/ferretdb/common/minio_util.py
@@ -0,0 +1,182 @@
+# 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 os
+import base64
+import random
+import string
+import mimetypes
+
+from minio import Minio
+from minio.error import S3Error
+from minio.commonconfig import CopySource
+
+def extract_mimetype(file):
+ mimetype, _ = mimetypes.guess_type(file)
+
+ if mimetype is None:
+ return "application/octet-stream"
+ else:
+ return mimetype
+
+def build_mo_client(host, port, access_key, secret_key):
+ """
+ Creates an Minio client pointing to the given MINIO HOST
+ :param host, minio host
+ :param port, minio api port normally it is the 9000
+ :param access_key user we are representing
+ :param secret_key to access minio
+ """
+ mo_client = Minio(f"{host}:{port}",access_key=access_key,secret_key=secret_key,secure=False)
+ return mo_client
+
+def upload_file(mo_client, file, bucket, object_name=None):
+ """Upload a file to an S3 bucket
+
+ :param file_name: File to upload
+ :param bucket: Bucket to upload to
+ :param object_name: S3 object name. If not specified then file_name is used
+ :return: a tuple with the Booleand str,
+ """
+
+ # If S3 object_name was not specified, use file_name
+ if object_name is None:
+ object_name = os.path.basename(file)
+
+ print(f"uploading {object_name} into bucket {bucket} from tmp_file {file}")
+
+ # Upload the file
+ try:
+ mimetype = extract_mimetype(object_name)
+ response = mo_client.fput_object(bucket_name=bucket,object_name=object_name,file_path=file,content_type=mimetype)
+ if response._object_name:
+ return True, response._object_name
+ except Exception as e:
+ print(e)
+ return False, str(e)
+ return False, "KO"
+
+def prepare_file_upload(username, filename, file_content_as_b64):
+ """ Creates a tmp area for the given user where the uploaded file will be stored under a random generated name
+ the fully qualified filename is taken into account only on the corresponding destination bucket.
+ param: username
+ param: filename
+ param: file_content_as_b64
+ return: a file object pointing to the tmp file
+ """
+ try:
+ user_tmp_folder = f"/tmp/{username}"
+ if not os.path.exists(user_tmp_folder):
+ os.makedirs(user_tmp_folder)
+ print(f"added tmp folder {user_tmp_folder}")
+
+ delete_files_in_directory_and_subdirectories(user_tmp_folder)
+ rnd_filename = get_random_string(20)
+ tmp_file = f"{user_tmp_folder}/{rnd_filename}"
+
+ with open(tmp_file, "wb") as f:
+ file_content=base64.b64decode(file_content_as_b64)
+ f.write(file_content)
+
+ if os.path.exists(tmp_file):
+ print(f"{tmp_file} stored successfully.")
+ return tmp_file
+ else:
+ return None
+ except Exception as e:
+ print("error preparing tmp_files",e)
+ return None
+
+def delete_files_in_directory_and_subdirectories(directory_path):
+ try:
+ for root, dirs, files in os.walk(directory_path):
+ for file in files:
+ file_path = os.path.join(root, file)
+ os.remove(file_path)
+ print("All files and subdirectories deleted successfully.")
+ except OSError:
+ print("Error occurred while deleting files and subdirectories.")
+
+def get_random_string(length):
+ # choose from all lowercase letter
+ letters = string.ascii_lowercase
+ return ''.join(random.choice(letters) for i in range(length))
+
+def rm_file(mo_client, bucket, file):
+ """ Remove a file from a bucket
+ :parma mo_client a minio client instance to execute the command
+ :param bucket the bucket name
+ :param file the file name
+ :return True if the file has been removed, False otherwise
+ """
+
+ try:
+ mo_client.remove_object(bucket, file)
+ return True
+ except Exception as e:
+ print(e)
+ return False
+ return False
+
+def cp_file(mo_client, orig_bucket, orig_file, dest_bucket, dest_file):
+ """Copy a file between two buckets
+
+ :param orig_bucket: Origin bucket
+ :param orig_file: Origin file
+ :param dest_bucket: Destination bucket
+ :param dest_file: Destination file
+ :return: name of the destination file is everything is ok. None otherwise
+ """
+ print(f"**** moving file {orig_file} from bucket {orig_bucket} to bucket {dest_bucket} with name {dest_file}")
+ try:
+ # copy an object from a bucket to another.
+ cp_result = mo_client.copy_object(
+ dest_bucket,
+ dest_file,
+ CopySource(orig_bucket, orig_file)
+ )
+ if cp_result.object_name:
+ return cp_result.object_name
+ except Exception as e:
+ print(e)
+ return None
+ return None
+
+def mv_file(mo_client, orig_bucket, orig_file, dest_bucket, dest_file):
+ """Move a file between two buckets
+
+ :param orig_bucket: Origin bucket
+ :param orig_file: Origin file
+ :param dest_bucket: Destination bucket
+ :param dest_file: Destination file
+ :return: name of the destination file is everything is ok. None otherwise
+ """
+ print(f"**** moving file {orig_file} from bucket {orig_bucket} to bucket {dest_bucket} with name {dest_file}")
+
+ # Upload the file
+ try:
+ # mv an object from a bucket to another.
+ object_name = cp_file(mo_client, orig_bucket, orig_file, dest_bucket, dest_file)
+ if object_name:
+ rm_file(mo_client,orig_bucket,orig_file)
+ return object_name
+ except Exception as e:
+ print(e)
+ return None
+ return None
+
diff --git a/actions/devel/ferretdb/common/util.py b/actions/devel/ferretdb/common/util.py
new file mode 100644
index 0000000..f1e8f39
--- /dev/null
+++ b/actions/devel/ferretdb/common/util.py
@@ -0,0 +1,35 @@
+# 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 mimetypes
+
+def get_env_value(user_data, key):
+ """
+ Check if inside the given user_data object there is an env item with the
+ given name and returns the value if any.
+ :param user_data
+ :param key
+ :return None if the given key it is not prese
+ """
+ envs = list(user_data['env'])
+
+ for env in envs:
+ if env['key'] == key:
+ return env['value']
+
+ return None
\ No newline at end of file
diff --git a/actions/devel/ferretdb/flatdict.py b/actions/devel/ferretdb/flatdict.py
new file mode 100644
index 0000000..834ce99
--- /dev/null
+++ b/actions/devel/ferretdb/flatdict.py
@@ -0,0 +1,506 @@
+# 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.
+
+"""FlatDict is a dict object that allows for single level, delimited
+key/value pair mapping of nested dictionaries.
+
+"""
+try:
+ from collections.abc import MutableMapping
+except ImportError: # pragma: nocover
+ from collections import MutableMapping
+import sys
+
+__version__ = '4.0.1'
+
+NO_DEFAULT = object()
+
+
+class FlatDict(MutableMapping):
+ """:class:`~flatdict.FlatDict` is a dictionary object that allows for
+ single level, delimited key/value pair mapping of nested dictionaries.
+ The default delimiter value is ``:`` but can be changed in the constructor
+ or by calling :meth:`FlatDict.set_delimiter`.
+
+ """
+ _COERCE = dict
+
+ def __init__(self, value=None, delimiter=':', dict_class=dict):
+ super(FlatDict, self).__init__()
+ self._values = dict_class()
+ self._delimiter = delimiter
+ self.update(value)
+
+ def __contains__(self, key):
+ """Check to see if the key exists, checking for both delimited and
+ not delimited key values.
+
+ :param mixed key: The key to check for
+
+ """
+ if self._has_delimiter(key):
+ pk, ck = key.split(self._delimiter, 1)
+ return pk in self._values and ck in self._values[pk]
+ return key in self._values
+
+ def __delitem__(self, key):
+ """Delete the item for the specified key, automatically dealing with
+ nested children.
+
+ :param mixed key: The key to use
+ :raises: KeyError
+
+ """
+ if key not in self:
+ raise KeyError
+ if self._has_delimiter(key):
+ pk, ck = key.split(self._delimiter, 1)
+ del self._values[pk][ck]
+ if not self._values[pk]:
+ del self._values[pk]
+ else:
+ del self._values[key]
+
+ def __eq__(self, other):
+ """Check for equality against the other value
+
+ :param other: The value to compare
+ :type other: FlatDict
+ :rtype: bool
+ :raises: TypeError
+
+ """
+ if isinstance(other, dict):
+ return self.as_dict() == other
+ elif not isinstance(other, self.__class__):
+ raise TypeError
+ return self.as_dict() == other.as_dict()
+
+ def __ne__(self, other):
+ """Check for inequality against the other value
+
+ :param other: The value to compare
+ :type other: dict or FlatDict
+ :rtype: bool
+
+ """
+ return not self.__eq__(other)
+
+ def __getitem__(self, key):
+ """Get an item for the specified key, automatically dealing with
+ nested children.
+
+ :param mixed key: The key to use
+ :rtype: mixed
+ :raises: KeyError
+
+ """
+ values = self._values
+ key = [key] if isinstance(key, int) else key.split(self._delimiter)
+ for part in key:
+ values = values[part]
+ return values
+
+ def __iter__(self):
+ """Iterate over the flat dictionary key and values
+
+ :rtype: Iterator
+ :raises: RuntimeError
+
+ """
+ return iter(self.keys())
+
+ def __len__(self):
+ """Return the number of items.
+
+ :rtype: int
+
+ """
+ return len(self.keys())
+
+ def __reduce__(self):
+ """Return state information for pickling
+
+ :rtype: tuple
+
+ """
+ return type(self), (self.as_dict(), self._delimiter)
+
+ def __repr__(self):
+ """Return the string representation of the instance.
+
+ :rtype: str
+
+ """
+ return '<{} id={} {}>"'.format(self.__class__.__name__, id(self),
+ str(self))
+
+ def __setitem__(self, key, value):
+ """Assign the value to the key, dynamically building nested
+ FlatDict items where appropriate.
+
+ :param mixed key: The key for the item
+ :param mixed value: The value for the item
+ :raises: TypeError
+
+ """
+ if isinstance(value, self._COERCE) and not isinstance(value, FlatDict):
+ value = self.__class__(value, self._delimiter)
+ if self._has_delimiter(key):
+ pk, ck = key.split(self._delimiter, 1)
+ if pk not in self._values:
+ self._values[pk] = self.__class__({ck: value}, self._delimiter)
+ return
+ elif not isinstance(self._values[pk], FlatDict):
+ raise TypeError(
+ 'Assignment to invalid type for key {}'.format(pk))
+ self._values[pk][ck] = value
+ else:
+ self._values[key] = value
+
+ def __str__(self):
+ """Return the string value of the instance.
+
+ :rtype: str
+
+ """
+ return '{{{}}}'.format(', '.join(
+ ['{!r}: {!r}'.format(k, self[k]) for k in self.keys()]))
+
+ def as_dict(self):
+ """Return the :class:`~flatdict.FlatDict` as a :class:`dict`
+
+ :rtype: dict
+
+ """
+ out = dict({})
+ for key in self.keys():
+ if self._has_delimiter(key):
+ pk, ck = key.split(self._delimiter, 1)
+ if self._has_delimiter(ck):
+ ck = ck.split(self._delimiter, 1)[0]
+ if isinstance(self._values[pk], FlatDict) and pk not in out:
+ out[pk] = {}
+ if isinstance(self._values[pk][ck], FlatDict):
+ out[pk][ck] = self._values[pk][ck].as_dict()
+ else:
+ out[pk][ck] = self._values[pk][ck]
+ else:
+ out[key] = self._values[key]
+ return out
+
+ def clear(self):
+ """Remove all items from the flat dictionary."""
+ self._values.clear()
+
+ def copy(self):
+ """Return a shallow copy of the flat dictionary.
+
+ :rtype: flatdict.FlatDict
+
+ """
+ return self.__class__(self.as_dict(), delimiter=self._delimiter)
+
+ def get(self, key, d=None):
+ """Return the value for key if key is in the flat dictionary, else
+ default. If default is not given, it defaults to ``None``, so that this
+ method never raises :exc:`KeyError`.
+
+ :param mixed key: The key to get
+ :param mixed d: The default value
+ :rtype: mixed
+
+ """
+ try:
+ return self.__getitem__(key)
+ except KeyError:
+ return d
+
+ def items(self):
+ """Return a copy of the flat dictionary's list of ``(key, value)``
+ pairs.
+
+ .. note:: CPython implementation detail: Keys and values are listed in
+ an arbitrary order which is non-random, varies across Python
+ implementations, and depends on the flat dictionary's history of
+ insertions and deletions.
+
+ :rtype: list
+
+ """
+ return [(k, self.__getitem__(k)) for k in self.keys()]
+
+ def iteritems(self):
+ """Return an iterator over the flat dictionary's (key, value) pairs.
+ See the note for :meth:`flatdict.FlatDict.items`.
+
+ Using ``iteritems()`` while adding or deleting entries in the flat
+ dictionary may raise :exc:`RuntimeError` or fail to iterate over all
+ entries.
+
+ :rtype: Iterator
+ :raises: RuntimeError
+
+ """
+ for item in self.items():
+ yield item
+
+ def iterkeys(self):
+ """Iterate over the flat dictionary's keys. See the note for
+ :meth:`flatdict.FlatDict.items`.
+
+ Using ``iterkeys()`` while adding or deleting entries in the flat
+ dictionary may raise :exc:`RuntimeError` or fail to iterate over all
+ entries.
+
+ :rtype: Iterator
+ :raises: RuntimeError
+
+ """
+ for key in self.keys():
+ yield key
+
+ def itervalues(self):
+ """Return an iterator over the flat dictionary's values. See the note
+ :meth:`flatdict.FlatDict.items`.
+
+ Using ``itervalues()`` while adding or deleting entries in the flat
+ dictionary may raise a :exc:`RuntimeError` or fail to iterate over all
+ entries.
+
+ :rtype: Iterator
+ :raises: RuntimeError
+
+ """
+ for value in self.values():
+ yield value
+
+ def keys(self):
+ """Return a copy of the flat dictionary's list of keys.
+ See the note for :meth:`flatdict.FlatDict.items`.
+
+ :rtype: list
+
+ """
+ keys = []
+
+ for key, value in self._values.items():
+ if isinstance(value, (FlatDict, dict)):
+ nested = [
+ self._delimiter.join([str(key), str(k)])
+ for k in value.keys()]
+ keys += nested if nested else [key]
+ else:
+ keys.append(key)
+
+ return keys
+
+ def pop(self, key, default=NO_DEFAULT):
+ """If key is in the flat dictionary, remove it and return its value,
+ else return default. If default is not given and key is not in the
+ dictionary, :exc:`KeyError` is raised.
+
+ :param mixed key: The key name
+ :param mixed default: The default value
+ :rtype: mixed
+
+ """
+ if key not in self and default != NO_DEFAULT:
+ return default
+ value = self[key]
+ self.__delitem__(key)
+ return value
+
+ def setdefault(self, key, default):
+ """If key is in the flat dictionary, return its value. If not,
+ insert key with a value of default and return default.
+ default defaults to ``None``.
+
+ :param mixed key: The key name
+ :param mixed default: The default value
+ :rtype: mixed
+
+ """
+ if key not in self:
+ self.__setitem__(key, default)
+ return self.__getitem__(key)
+
+ def set_delimiter(self, delimiter):
+ """Override the default or passed in delimiter with a new value. If
+ the requested delimiter already exists in a key, a :exc:`ValueError`
+ will be raised.
+
+ :param str delimiter: The delimiter to use
+ :raises: ValueError
+
+ """
+ for key in self.keys():
+ if delimiter in key:
+ raise ValueError('Key {!r} collides with delimiter {!r}', key,
+ delimiter)
+ self._delimiter = delimiter
+ for key in self._values.keys():
+ if isinstance(self._values[key], FlatDict):
+ self._values[key].set_delimiter(delimiter)
+
+ def update(self, other=None, **kwargs):
+ """Update the flat dictionary with the key/value pairs from other,
+ overwriting existing keys.
+
+ ``update()`` accepts either another flat dictionary object or an
+ iterable of key/value pairs (as tuples or other iterables of length
+ two). If keyword arguments are specified, the flat dictionary is then
+ updated with those key/value pairs: ``d.update(red=1, blue=2)``.
+
+ :param iterable other: Iterable of key, value pairs
+ :rtype: None
+
+ """
+ [self.__setitem__(k, v) for k, v in dict(other or kwargs).items()]
+
+ def values(self):
+ """Return a copy of the flat dictionary's list of values. See the note
+ for :meth:`flatdict.FlatDict.items`.
+
+ :rtype: list
+
+ """
+ return [self.__getitem__(k) for k in self.keys()]
+
+ def _has_delimiter(self, key):
+ """Checks to see if the key contains the delimiter.
+
+ :rtype: bool
+
+ """
+ return isinstance(key, str) and self._delimiter in key
+
+
+class FlatterDict(FlatDict):
+ """Like :class:`~flatdict.FlatDict` but also coerces lists and sets
+ to child-dict instances with the offset as the key. Alternative to
+ the implementation added in v1.2 of FlatDict.
+
+ """
+ _COERCE = list, tuple, set, dict, FlatDict
+ _ARRAYS = list, set, tuple
+
+ def __init__(self, value=None, delimiter=':', dict_class=dict):
+ self.original_type = type(value)
+ if self.original_type in self._ARRAYS:
+ value = {str(i): v for i, v in enumerate(value)}
+ super(FlatterDict, self).__init__(value, delimiter, dict_class)
+
+ def __setitem__(self, key, value):
+ """Assign the value to the key, dynamically building nested
+ FlatDict items where appropriate.
+
+ :param mixed key: The key for the item
+ :param mixed value: The value for the item
+ :raises: TypeError
+
+ """
+ if isinstance(value, self._COERCE) and \
+ not isinstance(value, FlatterDict):
+ value = self.__class__(value, self._delimiter)
+ if self._has_delimiter(key):
+ pk, ck = key.split(self._delimiter, 1)
+ if pk not in self._values:
+ self._values[pk] = self.__class__({ck: value}, self._delimiter)
+ return
+ if getattr(self._values[pk], 'original_type',
+ None) in self._ARRAYS:
+ try:
+ k, cck = ck.split(self._delimiter, 1)
+ int(k)
+ except ValueError:
+ raise TypeError(
+ 'Assignment to invalid type for key {}{}{}'.format(
+ pk, self._delimiter, ck))
+ self._values[pk][k][cck] = value
+ return
+ elif not isinstance(self._values[pk], FlatterDict):
+ raise TypeError(
+ 'Assignment to invalid type for key {}'.format(pk))
+ self._values[pk][ck] = value
+ else:
+ self._values[key] = value
+
+ def as_dict(self):
+ """Return the :class:`~flatdict.FlatterDict` as a nested
+ :class:`dict`.
+
+ :rtype: dict
+
+ """
+ out = {}
+ for key in self.keys():
+ if self._has_delimiter(key):
+ pk, ck = key.split(self._delimiter, 1)
+ if self._has_delimiter(ck):
+ ck = ck.split(self._delimiter, 1)[0]
+ if isinstance(self._values[pk], FlatterDict) and pk not in out:
+ if self._values[pk].original_type == tuple:
+ out[pk] = tuple(self._child_as_list(pk))
+ elif self._values[pk].original_type == list:
+ out[pk] = self._child_as_list(pk)
+ elif self._values[pk].original_type == set:
+ out[pk] = set(self._child_as_list(pk))
+ elif self._values[pk].original_type == dict:
+ out[pk] = self._values[pk].as_dict()
+ else:
+ if isinstance(self._values[key], FlatterDict):
+ out[key] = self._values[key].original_type()
+ else:
+ out[key] = self._values[key]
+ return out
+
+ def _child_as_list(self, pk, ck=None):
+ """Returns a list of values from the child FlatterDict instance
+ with string based integer keys.
+
+ :param str pk: The parent key
+ :param str ck: The child key, optional
+ :rtype: list
+
+ """
+ if ck is None:
+ subset = self._values[pk]
+ else:
+ subset = self._values[pk][ck]
+ # Check if keys has delimiter, which implies deeply nested dict
+ keys = subset.keys()
+ if any(self._has_delimiter(k) for k in keys):
+ out = []
+ split_keys = {k.split(self._delimiter)[0] for k in keys}
+ for k in sorted(split_keys, key=lambda x: int(x)):
+ if subset[k].original_type == tuple:
+ out.append(tuple(self._child_as_list(pk, k)))
+ elif subset[k].original_type == list:
+ out.append(self._child_as_list(pk, k))
+ elif subset[k].original_type == set:
+ out.append(set(self._child_as_list(pk, k)))
+ elif subset[k].original_type == dict:
+ out.append(subset[k].as_dict())
+ return out
+
+ # Python prior 3.6 does not guarantee insertion order, remove it after
+ # EOL python 3.5 - 2020-09-13
+ if sys.version_info[0:2] < (3, 6): # pragma: nocover
+ return [subset[k] for k in sorted(keys, key=lambda x: int(x))]
+ else:
+ return [subset[k] for k in keys]
diff --git a/actions/devel/ferretdb/nuvolaris/config.py b/actions/devel/ferretdb/nuvolaris/config.py
new file mode 100644
index 0000000..787d4a7
--- /dev/null
+++ b/actions/devel/ferretdb/nuvolaris/config.py
@@ -0,0 +1,202 @@
+# 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 flatdict, json, os
+import logging
+
+_config = {}
+
+# define a configuration
+# the configuration is a map, followed by a list of labels
+# the map can be a serialized json and will be flattened to a map of values.
+# you can have only a configuration active at a time
+# if you want to set a new configuration you have to clean it
+def configure(spec: dict):
+ global _config
+ _config = dict(flatdict.FlatDict(spec, delimiter="."))
+
+ #forces autodetect of kube if not provided
+ if not exists('nuvolaris.kube'):
+ put('nuvolaris.kube','auto')
+
+ return True
+
+def clean():
+ global _config
+ _config = {}
+
+def exists(key):
+ return key in _config
+
+def get(key, envvar=None, defval=None):
+ val = _config.get(key)
+
+ if envvar and envvar in os.environ:
+ val = os.environ[envvar]
+
+ if val:
+ return val
+
+ return defval
+
+def put(key, value):
+ _config[key] = value
+ return True
+
+def delete(key):
+ if key in _config:
+ del _config[key]
+ return True
+
+def getall(prefix=""):
+ res = {}
+ for key in _config.keys():
+ if key.startswith(prefix):
+ res[key] = _config[key]
+ return res
+
+def keys(prefix=""):
+ res = []
+ if _config:
+ for key in _config.keys():
+ if key.startswith(prefix):
+ res.append(key)
+ return res
+
+def detect_labels(labels=None):
+ # skip autodetection of nuvolaris.kube if already available in _config
+ if exists('nuvolaris.kube') and get('nuvolaris.kube') != 'auto':
+ logging.info(f"*** configuration provided already a nuvolaris.kube={get('nuvolaris.kube')}")
+ return {}
+
+ # read labels if not available
+ if not labels:
+ import nuvolaris.kube as kube
+ labels = kube.kubectl("get", "nodes", jsonpath='{.items[].metadata.labels}')
+
+ res = {}
+ kube = None
+ for i in labels:
+ for j in list(i.keys()):
+ if j.startswith("nuvolaris.io/"):
+ key = f"nuvolaris.{j[13:]}"
+ res[key] = i[j]
+ _config[key] = i[j]
+
+ for i in labels:
+ for j in list(i.keys()):
+ # detect the kube type
+ if j.find("eksctl.io") >= 0:
+ kube ="eks"
+ break
+ if j.find("k8s.io/cloud-provider-aws") >= 0:
+ kube ="eks"
+ break
+ elif j.find("microk8s.io") >= 0:
+ kube = "microk8s"
+ break
+ elif j.find("lke.linode.com") >=0:
+ kube = "lks"
+ break
+ elif j.find("node.openshift.io") >=0:
+ kube = "openshift"
+ break
+ elif j.endswith("kubernetes.io/instance-type") and i[j] == "k3s":
+ kube = "k3s"
+ break
+ elif j.find("cloud.google.com/gke") >=0:
+ kube = "gke"
+ break
+ elif j.find("kubernetes.azure.com") >=0:
+ kube = "aks"
+ break
+ # assign all the 'nuvolaris.io' labels
+
+ if kube:
+ res["nuvolaris.kube"] = kube
+ _config["nuvolaris.kube"] = kube
+ return res
+
+ # defaults to generic if it is not yet detected
+ if exists('nuvolaris.kube') and get('nuvolaris.kube') == 'auto':
+ _config["nuvolaris.kube"] = "generic"
+ res["nuvolaris.kube"] = "generic"
+
+ return res
+
+def detect_storage(storages=None):
+ res = {}
+ if not storages:
+ import nuvolaris.util as util
+
+ try:
+ detect_storageclass = True
+ detect_provisioner = True
+
+ # skips autodetection if already provided
+ if exists('nuvolaris.storageclass') and get('nuvolaris.storageclass') != 'auto':
+ logging.info(f"*** configuration provided already a nuvolaris.storageclass={get('nuvolaris.storageclass')}")
+ detect_storageclass = False
+
+ if exists('nuvolaris.provisioner') and get('nuvolaris.provisioner') != 'auto':
+ logging.info(f"*** configuration provided already a nuvolaris.provisioner={get('nuvolaris.provisioner')}")
+ detect_provisioner = False
+
+ if not detect_storageclass and not detect_provisioner:
+ return res
+
+ if detect_storageclass:
+ storage_class = util.get_default_storage_class()
+ if storage_class:
+ res['nuvolaris.storageclass'] = storage_class
+ _config['nuvolaris.storageclass'] = storage_class
+
+ if detect_provisioner:
+ provisioner = util.get_default_storage_provisioner()
+ if(provisioner):
+ res['nuvolaris.provisioner'] = provisioner
+ _config['nuvolaris.provisioner'] = provisioner
+ except:
+ pass
+ return res
+
+def detect_env():
+ _config['operator.image'] = os.environ.get("OPERATOR_IMAGE", "missing-OPERATOR_IMAGE")
+ _config['operator.tag'] = os.environ.get("OPERATOR_TAG", "missing-OPERATOR_TAG")
+
+ # skip autodetection of controller.image if already configure into whisk.yaml
+ if not exists('controller.image'):
+ _config['controller.image'] = os.environ.get("CONTROLLER_IMAGE", "missing-CONTROLLER_IMAGE")
+ _config['controller.tag'] = os.environ.get("CONTROLLER_TAG", "missing-CONTROLLER_TAG")
+ else:
+ logging.warn(f"OW controller image detection skipped. Using {get('controller.image')}")
+
+ if not exists('invoker.image'):
+ _config['invoker.image'] = os.environ.get("INVOKER_IMAGE", "missing-INVOKER_IMAGE")
+ _config['invoker.tag'] = os.environ.get("INVOKER_TAG", "missing-INVOKER_TAG")
+ else:
+ logging.warn(f"OW invoker image detection skipped. Using {get('invoker.image')}")
+
+def detect():
+ detect_storage()
+ detect_labels()
+ detect_env()
+
+def dump_config():
+ import nuvolaris.config as cfg
+ for k in cfg.getall():
+ logging.debug(f"{k} = {cfg.get(k)}")
diff --git a/actions/devel/ferretdb/nuvolaris/couchdb_util.py b/actions/devel/ferretdb/nuvolaris/couchdb_util.py
new file mode 100644
index 0000000..3adb6b9
--- /dev/null
+++ b/actions/devel/ferretdb/nuvolaris/couchdb_util.py
@@ -0,0 +1,164 @@
+# 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 os, json, time, sys, logging
+import requests as req
+import nuvolaris.config as cfg
+
+class CouchDB:
+ def __init__(self):
+ self.db_protocol = "http"
+ self.db_prefix = "nuvolaris_"
+ self.db_port = cfg.get("couchdb.port", "COUCHDB_SERVICE_PORT", "5984")
+ self.db_host = cfg.get("couchdb.host", "COUCHDB_SERVICE_HOST", "couchdb")
+ self.db_username = cfg.get("couchdb.admin.user", "COUCHDB_ADMIN_USER", "whisk_admin")
+ self.db_password = cfg.get("couchdb.admin.password", "COUCHDB_ADMIN_PASSWORD", "some_passw0rd")
+ self.db_auth = req.auth.HTTPBasicAuth(self.db_username,self.db_password)
+ self.db_url = f"{self.db_protocol}://{self.db_host}:{self.db_port}"
+ self.db_base = f"{self.db_url}/{self.db_prefix}"
+ self.db_session = req.Session()
+ self.db_session.auth = self.db_auth
+
+ def wait_db_ready(self, max_seconds):
+ logging.info("entering CouchDB.wait_db_ready()")
+ start = time.time()
+ delta = 0
+ session = req.Session()
+ while delta < max_seconds:
+ try:
+ 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
+ except Exception as e:
+ logging.info(f"waiting since: {delta} seconds")
+ delta = int(time.time() - start)
+ time.sleep(1)
+ return False
+
+ # check if database exists, return boolean
+ def check_db(self, database):
+ url = f"{self.db_base}{database}"
+ r = self.db_session.head(url)
+ return r.status_code == 200
+
+ # delete database, return true if ok
+ def delete_db(self, database):
+ url = f"{self.db_base}{database}"
+ r = self.db_session.delete(url)
+ return r.status_code == 200
+
+ # create db, return true if ok
+ def create_db(self, database):
+ url = f"{self.db_base}{database}"
+ r = self.db_session.put(url)
+ return r.status_code == 201
+
+ # database="subjects"
+ def recreate_db(self, database, recreate=False):
+ msg = "recreate_db:"
+ exists = self.check_db(database)
+ if recreate and exists:
+ msg += " deleted"
+ self.delete_db(database)
+ if recreate or not exists:
+ msg += " created"
+ self.create_db(database)
+ return msg
+
+ def get_doc(self, database, id, user=None, password="", no_auth=False):
+ url = f"{self.db_base}{database}/{id}"
+ session = req.Session()
+ if no_auth:
+ db_auth=None
+ elif user:
+ db_auth=req.auth.HTTPBasicAuth(user, password)
+ else:
+ db_auth = self.db_auth
+ session.auth = db_auth
+ r = session.get(url)
+ if r.status_code == 200:
+ return json.loads(r.text)
+ return None
+
+ def update_doc(self, database, doc):
+ if '_id' in doc:
+ url = f"{self.db_base}{database}/{doc['_id']}"
+ cur = self.get_doc(database, doc['_id'])
+ if cur and '_rev' in cur:
+ doc['_rev'] = cur['_rev']
+ r = self.db_session.put(url, json=doc)
+ else:
+ r = self.db_session.put(url, json=doc)
+ return r.status_code in [200,201]
+ return False
+
+ def delete_doc(self, database, id):
+ cur = self.get_doc(database, id)
+ if cur and '_rev' in cur:
+ url = f"{self.db_base}{database}/{cur['_id']}?rev={cur['_rev']}"
+ r = self.db_session.delete(url)
+ return r.status_code == 200
+ return False
+
+ def configure_single_node(self):
+ url = f"{self.db_url}/_cluster_setup"
+ data = {"action": "enable_single_node", "singlenode": True, "bind_address": "0.0.0.0", "port": 5984}
+ r = self.db_session.post(url, json=data)
+ return r.status_code == 201
+
+ def configure_no_reduce_limit(self):
+ url = f"{self.db_url}/_node/_local/_config/query_server_config/reduce_limit"
+ data=b'"false"'
+ r = self.db_session.put(url, data=data)
+ return r.status_code == 200
+
+ def add_user(self, username: str, password: str):
+ userpass = {"name": username, "password": password, "roles": [], "type": "user"}
+ url = f"{self.db_url}/_users/org.couchdb.user:{username}"
+ res = self.db_session.put(url, json=userpass)
+ return res.status_code in [200, 201, 421]
+
+ #def add_role(self, database: str, members: list[str] = [], admins: list[str] =[]):
+ def add_role(self, database: str, members = [], admins =[]):
+ roles = {"admins": { "names": admins, "roles": [] }, "members": { "names": members, "roles": [] } }
+ url = f"{self.db_base}{database}/_security"
+ res = self.db_session.put(url, json=roles)
+ return res.status_code in [200, 201, 421]
+
+#
+# Submit a POST request to the _find endpoint using the specified selector
+#
+ def find_doc(self, database, selector, user=None, password="", no_auth=False):
+ url = f"{self.db_base}{database}/_find"
+ headers = {'Content-Type': 'application/json'}
+ session = req.Session()
+
+ if no_auth:
+ db_auth=None
+ elif user:
+ db_auth=req.auth.HTTPBasicAuth(user, password)
+ else:
+ db_auth = self.db_auth
+
+ session.auth=db_auth
+ r = session.post(url, headers=headers, data=selector)
+ if r.status_code == 200:
+ return json.loads(r.text)
+
+ logging.warn(f"query to {url} failed with {r.status_code}. Body {r.text}")
+ return None
\ No newline at end of file
diff --git a/actions/devel/minio/__main__.py b/actions/devel/minio/__main__.py
new file mode 100644
index 0000000..14f37df
--- /dev/null
+++ b/actions/devel/minio/__main__.py
@@ -0,0 +1,77 @@
+# 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 nuvolaris.config as cfg
+import nuvolaris.couchdb_util as cu
+import logging, json
+
+from common.authorize import Authorize
+from command.minio import Minio
+from common.command_data import CommandData
+from base64 import b64decode
+
+class ApiError(Exception):
+ pass
+
+def build_error(message: str):
+ return {
+ "statusCode": 400,
+ "body": message
+ }
+
+def build_response(data:CommandData):
+ meta_data = data.get_metadata()
+ return {
+ "statusCode": meta_data['status'],
+ "body": meta_data['result']
+ }
+
+def parse_body(args):
+ try:
+ return b64decode(args['__ow_body']).decode().strip()
+ except Exception as e:
+ print(e)
+ raise ApiError("could not parse __ow_body as base64")
+
+def main(args):
+ """
+ Action implementing a generic command wrapper for the nuv devel plugin. The action must be called with a POST request receiving a JSON
+ payload similar to this one
+ {
+ "command":"ls",
+ "args":[
+ "<bucket-name>"
+ ]
+ }
+
+ the invoker must provide a x-impersonate-auth header containing the Openwhisk BASIC authentication of the wsku/user the action should impersonate
+ when submitting the command. Every specific provider will support specific request type and command.
+ WARNING: the body will be received as base64 encoded string as this action will be deployed with --web raw enabled flag
+ """
+ headers = args['__ow_headers']
+ if('x-impersonate-auth' not in headers):
+ return build_error("invalid request, missing mandatory header: x-impersonate-auth")
+
+ if(len(args['__ow_body']) == 0):
+ return build_error("invalid request, no command payload received")
+
+ try:
+ user_data = Authorize(args['couchdb_host'],args['couchdb_user'],args['couchdb_password']).login(headers['x-impersonate-auth'])
+ cmd = CommandData(json.loads(parse_body(args)))
+ return build_response(Minio(user_data).execute(cmd))
+ except Exception as e:
+ return build_error(f"failed to execute nuv devel minio. Reason: {str(e)}")
\ No newline at end of file
diff --git a/actions/devel/minio/command/minio.py b/actions/devel/minio/command/minio.py
new file mode 100644
index 0000000..252f375
--- /dev/null
+++ b/actions/devel/minio/command/minio.py
@@ -0,0 +1,170 @@
+# 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 common.util as ut
+import common.minio_util as mutil
+import json
+import re
+
+from common.command_data import CommandData
+
+from minio.commonconfig import CopySource
+
+class Minio():
+ """
+ Implementation of a Minio Command executor. It will require
+ a user_data dictionary linked to a specific user.
+ """
+
+ def __init__(self, user_data):
+ self._user_data = user_data
+ self._minio_access_key= ut.get_env_value(user_data,"MINIO_ACCESS_KEY")
+ self._minio_secret_key= ut.get_env_value(user_data,"MINIO_SECRET_KEY")
+ self._minio_host= ut.get_env_value(user_data,"MINIO_HOST")
+ self._minio_port= ut.get_env_value(user_data,"MINIO_PORT")
+ self._minio_data_bucket= ut.get_env_value(user_data,"MINIO_DATA_BUCKET")
+ self._minio_static_bucket= ut.get_env_value(user_data,"MINIO_STATIC_BUCKET")
+ self.validate()
+
+ def validate(self):
+ """
+ Validate that the provided user_data contains the appropriate
+ metadata for being able to submit a postgres command.
+ """
+ if not self._minio_access_key or not self._minio_secret_key or not self._minio_host or not self._minio_port:
+ raise Exception("user does not have valid MINIO environment set")
+
+ def _list_buckets(self, input:CommandData):
+ print("**** listing user buckets")
+ mo_client = mutil.build_mo_client(self._minio_host, self._minio_port,self._minio_access_key, self._minio_secret_key)
+ result = []
+
+ buckets = mo_client.list_buckets()
+
+ for bucket in buckets:
+ result.append({"bucket": bucket.name, "creation_date": str(bucket.creation_date)})
+
+ input.result(result)
+ input.status(200)
+
+ def _list_bucket_content(self,bucket,input:CommandData):
+ print(f"**** listing bucket {bucket} content")
+
+ mo_client = mutil.build_mo_client(self._minio_host, self._minio_port,self._minio_access_key, self._minio_secret_key)
+ result = []
+ objects = mo_client.list_objects(bucket_name=bucket, recursive= True)
+
+ for obj in objects:
+ result.append({"name":obj.object_name,"last_modified": str(obj.last_modified), "size":obj.size})
+
+ input.result(result)
+ input.status(200)
+
+ def _rm_bucket_object(self,bucket,filename,input:CommandData):
+ print(f"**** removing file {filename} inside bucket {bucket}")
+
+ mo_client = mutil.build_mo_client(self._minio_host, self._minio_port,self._minio_access_key, self._minio_secret_key)
+ mo_client.remove_object(bucket, filename)
+
+ result = {"result":"OK"}
+ input.result(result)
+ input.status(200)
+
+ def _mv_bucket_object(self,orig_bucket,orig_filename,dest_bucket,dest_filename,input:CommandData):
+ print(f"**** moving file {orig_filename} from bucket {orig_bucket} to bucket {dest_bucket} with name {dest_filename}")
+
+ mo_client = mutil.build_mo_client(self._minio_host, self._minio_port,self._minio_access_key, self._minio_secret_key)
+ object_name = mutil.mv_file(mo_client,orig_bucket,orig_filename,dest_bucket,dest_filename)
+
+ if object_name:
+ input.result({"name":object_name})
+ input.status(200)
+ else:
+ input.result(f"move operation failed")
+ input.status(400)
+
+ def _cp_bucket_object(self,orig_bucket,orig_filename,dest_bucket,dest_filename,input:CommandData):
+ print(f"**** copying file {orig_filename} from bucket {orig_bucket} to bucket {dest_bucket} with name {dest_filename}")
+
+ mo_client = mutil.build_mo_client(self._minio_host, self._minio_port,self._minio_access_key, self._minio_secret_key)
+ object_name = mutil.cp_file(mo_client,orig_bucket,orig_filename,dest_bucket,dest_filename)
+
+ if object_name:
+ input.result({"name":object_name})
+ input.status(200)
+ else:
+ input.result(f"move operation failed")
+ input.status(400)
+
+ def _clean_bucket_content(self,input:CommandData, bucket, pattern=".*", dry_run="true"):
+ """"
+ Removes matching content from the specified bucket
+ :param input the entire input command
+ :param bucket
+ :param pattern a regexp specifying the filename to match (defaults to .*)
+ :param dry_run a boolean to enable dry run execution (defaults to False)
+ """
+ print(f"**** cleaning bucket {bucket} content matching {pattern}. Dry run mode {dry_run}")
+
+ mo_client = mutil.build_mo_client(self._minio_host, self._minio_port,self._minio_access_key, self._minio_secret_key)
+ result = []
+ objects = mo_client.list_objects(bucket_name=bucket, recursive= True)
+
+ for obj in objects:
+ if re.search(pattern, obj.object_name):
+ if "true" in dry_run:
+ result.append({"name":obj.object_name,"last_modified": str(obj.last_modified), "size":obj.size})
+ else:
+ removed = mutil.rm_file(mo_client,bucket,obj.object_name)
+ if removed:
+ result.append({"name":obj.object_name})
+ else:
+ print(f"skipping {obj.object_name} as it does not match {pattern}")
+
+ input.result(result)
+ input.status(200)
+
+ def execute(self, input:CommandData):
+ print(f"**** Minio command to execute {input.command()}")
+ try:
+ input.status(400)
+ input.result(f"could not execute minio command {input.command()} invalid arguments.")
+
+ if "ls" in input.command() and not "args" in input.get_metadata():
+ self._list_buckets(input)
+
+ if "ls" in input.command() and "args" in input.get_metadata() and len(input.args()) == 1:
+ self._list_bucket_content(input.args()[0],input)
+
+ if "rm" in input.command() and "args" in input.get_metadata() and len(input.args()) == 2:
+ self._rm_bucket_object(input.args()[0],input.args()[1],input)
+
+ if "mv" in input.command() and "args" in input.get_metadata() and len(input.args()) == 4:
+ self._mv_bucket_object(input.args()[0],input.args()[1],input.args()[2],input.args()[3], input)
+
+ if "cp" in input.command() and "args" in input.get_metadata() and len(input.args()) == 4:
+ self._cp_bucket_object(input.args()[0],input.args()[1],input.args()[2],input.args()[3], input)
+
+ if "clean" in input.command() and "args" in input.get_metadata() and len(input.args()) == 3:
+ self._clean_bucket_content(input,input.args()[0],input.args()[1],input.args()[2])
+
+ except Exception as e:
+ input.result(f"could not execute minio command {e}")
+ input.status(400)
+
+ return input
\ No newline at end of file
diff --git a/actions/devel/minio/common/authorize.py b/actions/devel/minio/common/authorize.py
new file mode 100644
index 0000000..f7d716f
--- /dev/null
+++ b/actions/devel/minio/common/authorize.py
@@ -0,0 +1,162 @@
+# 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.
+#
+from base64 import b64decode, b64encode
+from urllib.parse import quote, unquote
+
+import nuvolaris.config as cfg
+import nuvolaris.couchdb_util as cu
+import json
+import common.util as ut
+
+USER_META_DBN = "users_metadata"
+SUBJECT_META_DBN = "subjects"
+
+class DecodeError(Exception):
+ pass
+
+class EncodeError(Exception):
+ pass
+
+class AuthorizationError(Exception):
+ pass
+
+class Authorize():
+
+ def __init__(self, cdb_host, cdb_user, cdb_pwd):
+ cfg.clean()
+ cfg.put("couchdb.host", cdb_host)
+ cfg.put("couchdb.admin.user", cdb_user)
+ cfg.put("couchdb.admin.password", cdb_pwd)
+ self._db = cu.CouchDB()
+
+ def encode(self,username, password):
+ """Returns an HTTP basic authentication encrypted string given a valid
+ username and password.
+ """
+ if ':' in username:
+ raise EncodeError
+
+ username_password = f'{quote(username)}:{quote(password)}'
+ return f'Basic {b64encode(username_password.encode()).decode()}'
+
+ def _parse_b64(self, encoded_str):
+ try:
+ username, password = b64decode(encoded_str).decode().split(':', 1)
+ except:
+ # fallback in case the token is not bas64 encoded
+ username, password = encoded_str.split(':', 1)
+
+ if not username or not password:
+ raise DecodeError("authentication token does not seems to be b64 encoded")
+
+ return username, password
+
+ def decode(self, encoded_str):
+ """Decode an encrypted HTTP basic authentication string. Returns a tuple of
+ the form (username, password), and raises a DecodeError exception if
+ nothing could be decoded.
+ """
+ split = encoded_str.strip().split(' ')
+
+ # If split is only one element, try to decode the username and password
+ # directly.
+ if len(split) == 1:
+ username, password = self._parse_b64(split[0])
+
+ # If there are only two elements, check the first and ensure it says
+ # 'basic' so that we know we're about to decode the right thing. If not,
+ # bail out.
+ elif len(split) == 2:
+ if split[0].strip().lower() == 'basic':
+ username, password = self._parse_b64(split[1])
+ else:
+ raise DecodeError("authentication token provides more than 2 elements. could not parse properly")
+
+ # If there are more than 2 elements, something crazy must be happening.
+ # Bail.
+ else:
+ raise DecodeError("unpredictable error parsing authentication token")
+
+ return unquote(username), unquote(password)
+
+ def fetch_subject(self, uuid: str, key: str):
+ """
+ Query the internal couchdb searching for the subject mathing the given uuid, key.
+ Normally these stored in wsk or wsku in the form uuid:key
+ :param uuid the OW subject uuid
+ :param key the OW subject key
+ :return a ubject document
+ """
+ print(f"searching for openwhisk subject {uuid}")
+ try:
+ selector= {
+ "selector": {"namespaces": {"$elemMatch": {"uuid": uuid,"key": key}}}
+ }
+
+ response = self._db.find_doc(SUBJECT_META_DBN, json.dumps(selector))
+
+ if(response['docs']):
+ docs = list(response['docs'])
+ if(len(docs) > 0):
+ print(f"Nuvolaris namespace for user {uuid} found. Returning Result.")
+ return docs[0]
+
+ print(f"Nuvolaris metadata for user {uuid} not found!")
+ return None
+ except Exception as e:
+ print(f"failed to query Nuvolaris metadata for user {uuid}. Reason: {e}")
+ return None
+
+ def fetch_user_data(self, username: str):
+ """
+ Query the internal couchdb searching for the given principal to retrieve all the
+ relevant metadata
+ """
+ print(f"searching for user {username} meta-data")
+ try:
+ selector = {"selector":{"login": {"$eq": username }}}
+ response = self._db.find_doc(USER_META_DBN, json.dumps(selector))
+
+ if(response['docs']):
+ docs = list(response['docs'])
+ if(len(docs) > 0):
+ print(f"Nuvolaris metadata for user {username} found. Returning Result.")
+ return docs[0]
+
+ print(f"Nuvolaris metadata for user {username} not found!")
+ return None
+ except Exception as e:
+ print(f"failed to query Nuvolaris metadata for user {username}. Reason: {e}")
+ return None
+
+ def login(self, authorization: str):
+ """
+ Attempt to login the user identified by the given Openwhisk authorization AUTH token as base64
+ """
+ uuid, key = self.decode(authorization)
+ subject = self.fetch_subject(uuid, key)
+
+ if not subject:
+ raise AuthorizationError("Openwhisk subject not found.")
+
+ user_data = self.fetch_user_data(subject['subject'])
+
+ if user_data:
+ return user_data
+
+ raise AuthorizationError("Could not retrieve user metadata.")
\ No newline at end of file
diff --git a/actions/devel/minio/common/command_data.py b/actions/devel/minio/common/command_data.py
new file mode 100644
index 0000000..44f3faa
--- /dev/null
+++ b/actions/devel/minio/common/command_data.py
@@ -0,0 +1,50 @@
+# 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 json
+import logging
+
+class CommandData:
+ _data = {}
+ _raw_data = {}
+
+ def __init__(self, cmd):
+ self._data = cmd
+ self._raw_data = cmd.copy()
+
+ def dump(self):
+ logging.debug(json.dumps(self._data))
+
+ def status(self, status):
+ self._data['status']=status
+
+ def result(self, result):
+ self._data['result']=result
+
+ def get_metadata(self):
+ return self._data
+
+ def get_raw_data(self):
+ return self._raw_data
+
+ def command(self):
+ return self._data['command']
+
+ def args(self):
+ return self._data['args']
+
+
diff --git a/actions/devel/minio/common/minio_util.py b/actions/devel/minio/common/minio_util.py
new file mode 100644
index 0000000..e1e8f90
--- /dev/null
+++ b/actions/devel/minio/common/minio_util.py
@@ -0,0 +1,182 @@
+# 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 os
+import base64
+import random
+import string
+import mimetypes
+
+from minio import Minio
+from minio.error import S3Error
+from minio.commonconfig import CopySource
+
+def extract_mimetype(file):
+ mimetype, _ = mimetypes.guess_type(file)
+
+ if mimetype is None:
+ return "application/octet-stream"
+ else:
+ return mimetype
+
+def build_mo_client(host, port, access_key, secret_key):
+ """
+ Creates an Minio client pointing to the given MINIO HOST
+ :param host, minio host
+ :param port, minio api port normally it is the 9000
+ :param access_key user we are representing
+ :param secret_key to access minio
+ """
+ mo_client = Minio(f"{host}:{port}",access_key=access_key,secret_key=secret_key,secure=False)
+ return mo_client
+
+def upload_file(mo_client, file, bucket, object_name=None):
+ """Upload a file to an S3 bucket
+
+ :param file_name: File to upload
+ :param bucket: Bucket to upload to
+ :param object_name: S3 object name. If not specified then file_name is used
+ :return: a tuple with the Booleand str,
+ """
+
+ # If S3 object_name was not specified, use file_name
+ if object_name is None:
+ object_name = os.path.basename(file)
+
+ print(f"uploading {object_name} into bucket {bucket} from tmp_file {file}")
+
+ # Upload the file
+ try:
+ mimetype = extract_mimetype(object_name)
+ response = mo_client.fput_object(bucket_name=bucket,object_name=object_name,file_path=file,content_type=mimetype)
+ if response._object_name:
+ return True, response._object_name
+ except Exception as e:
+ print(e)
+ return False, str(e)
+ return False, "KO"
+
+def prepare_file_upload(username, filename, file_content_as_b64):
+ """ Creates a tmp area for the given user where the uploaded file will be stored under a random generated name
+ the fully qualified filename is taken into account only on the corresponding destination bucket.
+ param: username
+ param: filename
+ param: file_content_as_b64
+ return: a file object pointing to the tmp file
+ """
+ try:
+ user_tmp_folder = f"/tmp/{username}"
+ if not os.path.exists(user_tmp_folder):
+ os.makedirs(user_tmp_folder)
+ print(f"added tmp folder {user_tmp_folder}")
+
+ delete_files_in_directory_and_subdirectories(user_tmp_folder)
+ rnd_filename = get_random_string(20)
+ tmp_file = f"{user_tmp_folder}/{rnd_filename}"
+
+ with open(tmp_file, "wb") as f:
+ file_content=base64.b64decode(file_content_as_b64)
+ f.write(file_content)
+
+ if os.path.exists(tmp_file):
+ print(f"{tmp_file} stored successfully.")
+ return tmp_file
+ else:
+ return None
+ except Exception as e:
+ print("error preparing tmp_files",e)
+ return None
+
+def delete_files_in_directory_and_subdirectories(directory_path):
+ try:
+ for root, dirs, files in os.walk(directory_path):
+ for file in files:
+ file_path = os.path.join(root, file)
+ os.remove(file_path)
+ print("All files and subdirectories deleted successfully.")
+ except OSError:
+ print("Error occurred while deleting files and subdirectories.")
+
+def get_random_string(length):
+ # choose from all lowercase letter
+ letters = string.ascii_lowercase
+ return ''.join(random.choice(letters) for i in range(length))
+
+def rm_file(mo_client, bucket, file):
+ """ Remove a file from a bucket
+ :parma mo_client a minio client instance to execute the command
+ :param bucket the bucket name
+ :param file the file name
+ :return True if the file has been removed, False otherwise
+ """
+
+ try:
+ mo_client.remove_object(bucket, file)
+ return True
+ except Exception as e:
+ print(e)
+ return False
+ return False
+
+def cp_file(mo_client, orig_bucket, orig_file, dest_bucket, dest_file):
+ """Copy a file between two buckets
+
+ :param orig_bucket: Origin bucket
+ :param orig_file: Origin file
+ :param dest_bucket: Destination bucket
+ :param dest_file: Destination file
+ :return: name of the destination file is everything is ok. None otherwise
+ """
+ print(f"**** moving file {orig_file} from bucket {orig_bucket} to bucket {dest_bucket} with name {dest_file}")
+ try:
+ # copy an object from a bucket to another.
+ cp_result = mo_client.copy_object(
+ dest_bucket,
+ dest_file,
+ CopySource(orig_bucket, orig_file)
+ )
+ if cp_result.object_name:
+ return cp_result.object_name
+ except Exception as e:
+ print(e)
+ return None
+ return None
+
+def mv_file(mo_client, orig_bucket, orig_file, dest_bucket, dest_file):
+ """Move a file between two buckets
+
+ :param orig_bucket: Origin bucket
+ :param orig_file: Origin file
+ :param dest_bucket: Destination bucket
+ :param dest_file: Destination file
+ :return: name of the destination file is everything is ok. None otherwise
+ """
+ print(f"**** moving file {orig_file} from bucket {orig_bucket} to bucket {dest_bucket} with name {dest_file}")
+
+ # Upload the file
+ try:
+ # mv an object from a bucket to another.
+ object_name = cp_file(mo_client, orig_bucket, orig_file, dest_bucket, dest_file)
+ if object_name:
+ rm_file(mo_client,orig_bucket,orig_file)
+ return object_name
+ except Exception as e:
+ print(e)
+ return None
+ return None
+
diff --git a/actions/devel/minio/common/util.py b/actions/devel/minio/common/util.py
new file mode 100644
index 0000000..f1e8f39
--- /dev/null
+++ b/actions/devel/minio/common/util.py
@@ -0,0 +1,35 @@
+# 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 mimetypes
+
+def get_env_value(user_data, key):
+ """
+ Check if inside the given user_data object there is an env item with the
+ given name and returns the value if any.
+ :param user_data
+ :param key
+ :return None if the given key it is not prese
+ """
+ envs = list(user_data['env'])
+
+ for env in envs:
+ if env['key'] == key:
+ return env['value']
+
+ return None
\ No newline at end of file
diff --git a/actions/devel/minio/flatdict.py b/actions/devel/minio/flatdict.py
new file mode 100644
index 0000000..834ce99
--- /dev/null
+++ b/actions/devel/minio/flatdict.py
@@ -0,0 +1,506 @@
+# 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.
+
+"""FlatDict is a dict object that allows for single level, delimited
+key/value pair mapping of nested dictionaries.
+
+"""
+try:
+ from collections.abc import MutableMapping
+except ImportError: # pragma: nocover
+ from collections import MutableMapping
+import sys
+
+__version__ = '4.0.1'
+
+NO_DEFAULT = object()
+
+
+class FlatDict(MutableMapping):
+ """:class:`~flatdict.FlatDict` is a dictionary object that allows for
+ single level, delimited key/value pair mapping of nested dictionaries.
+ The default delimiter value is ``:`` but can be changed in the constructor
+ or by calling :meth:`FlatDict.set_delimiter`.
+
+ """
+ _COERCE = dict
+
+ def __init__(self, value=None, delimiter=':', dict_class=dict):
+ super(FlatDict, self).__init__()
+ self._values = dict_class()
+ self._delimiter = delimiter
+ self.update(value)
+
+ def __contains__(self, key):
+ """Check to see if the key exists, checking for both delimited and
+ not delimited key values.
+
+ :param mixed key: The key to check for
+
+ """
+ if self._has_delimiter(key):
+ pk, ck = key.split(self._delimiter, 1)
+ return pk in self._values and ck in self._values[pk]
+ return key in self._values
+
+ def __delitem__(self, key):
+ """Delete the item for the specified key, automatically dealing with
+ nested children.
+
+ :param mixed key: The key to use
+ :raises: KeyError
+
+ """
+ if key not in self:
+ raise KeyError
+ if self._has_delimiter(key):
+ pk, ck = key.split(self._delimiter, 1)
+ del self._values[pk][ck]
+ if not self._values[pk]:
+ del self._values[pk]
+ else:
+ del self._values[key]
+
+ def __eq__(self, other):
+ """Check for equality against the other value
+
+ :param other: The value to compare
+ :type other: FlatDict
+ :rtype: bool
+ :raises: TypeError
+
+ """
+ if isinstance(other, dict):
+ return self.as_dict() == other
+ elif not isinstance(other, self.__class__):
+ raise TypeError
+ return self.as_dict() == other.as_dict()
+
+ def __ne__(self, other):
+ """Check for inequality against the other value
+
+ :param other: The value to compare
+ :type other: dict or FlatDict
+ :rtype: bool
+
+ """
+ return not self.__eq__(other)
+
+ def __getitem__(self, key):
+ """Get an item for the specified key, automatically dealing with
+ nested children.
+
+ :param mixed key: The key to use
+ :rtype: mixed
+ :raises: KeyError
+
+ """
+ values = self._values
+ key = [key] if isinstance(key, int) else key.split(self._delimiter)
+ for part in key:
+ values = values[part]
+ return values
+
+ def __iter__(self):
+ """Iterate over the flat dictionary key and values
+
+ :rtype: Iterator
+ :raises: RuntimeError
+
+ """
+ return iter(self.keys())
+
+ def __len__(self):
+ """Return the number of items.
+
+ :rtype: int
+
+ """
+ return len(self.keys())
+
+ def __reduce__(self):
+ """Return state information for pickling
+
+ :rtype: tuple
+
+ """
+ return type(self), (self.as_dict(), self._delimiter)
+
+ def __repr__(self):
+ """Return the string representation of the instance.
+
+ :rtype: str
+
+ """
+ return '<{} id={} {}>"'.format(self.__class__.__name__, id(self),
+ str(self))
+
+ def __setitem__(self, key, value):
+ """Assign the value to the key, dynamically building nested
+ FlatDict items where appropriate.
+
+ :param mixed key: The key for the item
+ :param mixed value: The value for the item
+ :raises: TypeError
+
+ """
+ if isinstance(value, self._COERCE) and not isinstance(value, FlatDict):
+ value = self.__class__(value, self._delimiter)
+ if self._has_delimiter(key):
+ pk, ck = key.split(self._delimiter, 1)
+ if pk not in self._values:
+ self._values[pk] = self.__class__({ck: value}, self._delimiter)
+ return
+ elif not isinstance(self._values[pk], FlatDict):
+ raise TypeError(
+ 'Assignment to invalid type for key {}'.format(pk))
+ self._values[pk][ck] = value
+ else:
+ self._values[key] = value
+
+ def __str__(self):
+ """Return the string value of the instance.
+
+ :rtype: str
+
+ """
+ return '{{{}}}'.format(', '.join(
+ ['{!r}: {!r}'.format(k, self[k]) for k in self.keys()]))
+
+ def as_dict(self):
+ """Return the :class:`~flatdict.FlatDict` as a :class:`dict`
+
+ :rtype: dict
+
+ """
+ out = dict({})
+ for key in self.keys():
+ if self._has_delimiter(key):
+ pk, ck = key.split(self._delimiter, 1)
+ if self._has_delimiter(ck):
+ ck = ck.split(self._delimiter, 1)[0]
+ if isinstance(self._values[pk], FlatDict) and pk not in out:
+ out[pk] = {}
+ if isinstance(self._values[pk][ck], FlatDict):
+ out[pk][ck] = self._values[pk][ck].as_dict()
+ else:
+ out[pk][ck] = self._values[pk][ck]
+ else:
+ out[key] = self._values[key]
+ return out
+
+ def clear(self):
+ """Remove all items from the flat dictionary."""
+ self._values.clear()
+
+ def copy(self):
+ """Return a shallow copy of the flat dictionary.
+
+ :rtype: flatdict.FlatDict
+
+ """
+ return self.__class__(self.as_dict(), delimiter=self._delimiter)
+
+ def get(self, key, d=None):
+ """Return the value for key if key is in the flat dictionary, else
+ default. If default is not given, it defaults to ``None``, so that this
+ method never raises :exc:`KeyError`.
+
+ :param mixed key: The key to get
+ :param mixed d: The default value
+ :rtype: mixed
+
+ """
+ try:
+ return self.__getitem__(key)
+ except KeyError:
+ return d
+
+ def items(self):
+ """Return a copy of the flat dictionary's list of ``(key, value)``
+ pairs.
+
+ .. note:: CPython implementation detail: Keys and values are listed in
+ an arbitrary order which is non-random, varies across Python
+ implementations, and depends on the flat dictionary's history of
+ insertions and deletions.
+
+ :rtype: list
+
+ """
+ return [(k, self.__getitem__(k)) for k in self.keys()]
+
+ def iteritems(self):
+ """Return an iterator over the flat dictionary's (key, value) pairs.
+ See the note for :meth:`flatdict.FlatDict.items`.
+
+ Using ``iteritems()`` while adding or deleting entries in the flat
+ dictionary may raise :exc:`RuntimeError` or fail to iterate over all
+ entries.
+
+ :rtype: Iterator
+ :raises: RuntimeError
+
+ """
+ for item in self.items():
+ yield item
+
+ def iterkeys(self):
+ """Iterate over the flat dictionary's keys. See the note for
+ :meth:`flatdict.FlatDict.items`.
+
+ Using ``iterkeys()`` while adding or deleting entries in the flat
+ dictionary may raise :exc:`RuntimeError` or fail to iterate over all
+ entries.
+
+ :rtype: Iterator
+ :raises: RuntimeError
+
+ """
+ for key in self.keys():
+ yield key
+
+ def itervalues(self):
+ """Return an iterator over the flat dictionary's values. See the note
+ :meth:`flatdict.FlatDict.items`.
+
+ Using ``itervalues()`` while adding or deleting entries in the flat
+ dictionary may raise a :exc:`RuntimeError` or fail to iterate over all
+ entries.
+
+ :rtype: Iterator
+ :raises: RuntimeError
+
+ """
+ for value in self.values():
+ yield value
+
+ def keys(self):
+ """Return a copy of the flat dictionary's list of keys.
+ See the note for :meth:`flatdict.FlatDict.items`.
+
+ :rtype: list
+
+ """
+ keys = []
+
+ for key, value in self._values.items():
+ if isinstance(value, (FlatDict, dict)):
+ nested = [
+ self._delimiter.join([str(key), str(k)])
+ for k in value.keys()]
+ keys += nested if nested else [key]
+ else:
+ keys.append(key)
+
+ return keys
+
+ def pop(self, key, default=NO_DEFAULT):
+ """If key is in the flat dictionary, remove it and return its value,
+ else return default. If default is not given and key is not in the
+ dictionary, :exc:`KeyError` is raised.
+
+ :param mixed key: The key name
+ :param mixed default: The default value
+ :rtype: mixed
+
+ """
+ if key not in self and default != NO_DEFAULT:
+ return default
+ value = self[key]
+ self.__delitem__(key)
+ return value
+
+ def setdefault(self, key, default):
+ """If key is in the flat dictionary, return its value. If not,
+ insert key with a value of default and return default.
+ default defaults to ``None``.
+
+ :param mixed key: The key name
+ :param mixed default: The default value
+ :rtype: mixed
+
+ """
+ if key not in self:
+ self.__setitem__(key, default)
+ return self.__getitem__(key)
+
+ def set_delimiter(self, delimiter):
+ """Override the default or passed in delimiter with a new value. If
+ the requested delimiter already exists in a key, a :exc:`ValueError`
+ will be raised.
+
+ :param str delimiter: The delimiter to use
+ :raises: ValueError
+
+ """
+ for key in self.keys():
+ if delimiter in key:
+ raise ValueError('Key {!r} collides with delimiter {!r}', key,
+ delimiter)
+ self._delimiter = delimiter
+ for key in self._values.keys():
+ if isinstance(self._values[key], FlatDict):
+ self._values[key].set_delimiter(delimiter)
+
+ def update(self, other=None, **kwargs):
+ """Update the flat dictionary with the key/value pairs from other,
+ overwriting existing keys.
+
+ ``update()`` accepts either another flat dictionary object or an
+ iterable of key/value pairs (as tuples or other iterables of length
+ two). If keyword arguments are specified, the flat dictionary is then
+ updated with those key/value pairs: ``d.update(red=1, blue=2)``.
+
+ :param iterable other: Iterable of key, value pairs
+ :rtype: None
+
+ """
+ [self.__setitem__(k, v) for k, v in dict(other or kwargs).items()]
+
+ def values(self):
+ """Return a copy of the flat dictionary's list of values. See the note
+ for :meth:`flatdict.FlatDict.items`.
+
+ :rtype: list
+
+ """
+ return [self.__getitem__(k) for k in self.keys()]
+
+ def _has_delimiter(self, key):
+ """Checks to see if the key contains the delimiter.
+
+ :rtype: bool
+
+ """
+ return isinstance(key, str) and self._delimiter in key
+
+
+class FlatterDict(FlatDict):
+ """Like :class:`~flatdict.FlatDict` but also coerces lists and sets
+ to child-dict instances with the offset as the key. Alternative to
+ the implementation added in v1.2 of FlatDict.
+
+ """
+ _COERCE = list, tuple, set, dict, FlatDict
+ _ARRAYS = list, set, tuple
+
+ def __init__(self, value=None, delimiter=':', dict_class=dict):
+ self.original_type = type(value)
+ if self.original_type in self._ARRAYS:
+ value = {str(i): v for i, v in enumerate(value)}
+ super(FlatterDict, self).__init__(value, delimiter, dict_class)
+
+ def __setitem__(self, key, value):
+ """Assign the value to the key, dynamically building nested
+ FlatDict items where appropriate.
+
+ :param mixed key: The key for the item
+ :param mixed value: The value for the item
+ :raises: TypeError
+
+ """
+ if isinstance(value, self._COERCE) and \
+ not isinstance(value, FlatterDict):
+ value = self.__class__(value, self._delimiter)
+ if self._has_delimiter(key):
+ pk, ck = key.split(self._delimiter, 1)
+ if pk not in self._values:
+ self._values[pk] = self.__class__({ck: value}, self._delimiter)
+ return
+ if getattr(self._values[pk], 'original_type',
+ None) in self._ARRAYS:
+ try:
+ k, cck = ck.split(self._delimiter, 1)
+ int(k)
+ except ValueError:
+ raise TypeError(
+ 'Assignment to invalid type for key {}{}{}'.format(
+ pk, self._delimiter, ck))
+ self._values[pk][k][cck] = value
+ return
+ elif not isinstance(self._values[pk], FlatterDict):
+ raise TypeError(
+ 'Assignment to invalid type for key {}'.format(pk))
+ self._values[pk][ck] = value
+ else:
+ self._values[key] = value
+
+ def as_dict(self):
+ """Return the :class:`~flatdict.FlatterDict` as a nested
+ :class:`dict`.
+
+ :rtype: dict
+
+ """
+ out = {}
+ for key in self.keys():
+ if self._has_delimiter(key):
+ pk, ck = key.split(self._delimiter, 1)
+ if self._has_delimiter(ck):
+ ck = ck.split(self._delimiter, 1)[0]
+ if isinstance(self._values[pk], FlatterDict) and pk not in out:
+ if self._values[pk].original_type == tuple:
+ out[pk] = tuple(self._child_as_list(pk))
+ elif self._values[pk].original_type == list:
+ out[pk] = self._child_as_list(pk)
+ elif self._values[pk].original_type == set:
+ out[pk] = set(self._child_as_list(pk))
+ elif self._values[pk].original_type == dict:
+ out[pk] = self._values[pk].as_dict()
+ else:
+ if isinstance(self._values[key], FlatterDict):
+ out[key] = self._values[key].original_type()
+ else:
+ out[key] = self._values[key]
+ return out
+
+ def _child_as_list(self, pk, ck=None):
+ """Returns a list of values from the child FlatterDict instance
+ with string based integer keys.
+
+ :param str pk: The parent key
+ :param str ck: The child key, optional
+ :rtype: list
+
+ """
+ if ck is None:
+ subset = self._values[pk]
+ else:
+ subset = self._values[pk][ck]
+ # Check if keys has delimiter, which implies deeply nested dict
+ keys = subset.keys()
+ if any(self._has_delimiter(k) for k in keys):
+ out = []
+ split_keys = {k.split(self._delimiter)[0] for k in keys}
+ for k in sorted(split_keys, key=lambda x: int(x)):
+ if subset[k].original_type == tuple:
+ out.append(tuple(self._child_as_list(pk, k)))
+ elif subset[k].original_type == list:
+ out.append(self._child_as_list(pk, k))
+ elif subset[k].original_type == set:
+ out.append(set(self._child_as_list(pk, k)))
+ elif subset[k].original_type == dict:
+ out.append(subset[k].as_dict())
+ return out
+
+ # Python prior 3.6 does not guarantee insertion order, remove it after
+ # EOL python 3.5 - 2020-09-13
+ if sys.version_info[0:2] < (3, 6): # pragma: nocover
+ return [subset[k] for k in sorted(keys, key=lambda x: int(x))]
+ else:
+ return [subset[k] for k in keys]
diff --git a/actions/devel/minio/nuvolaris/config.py b/actions/devel/minio/nuvolaris/config.py
new file mode 100644
index 0000000..787d4a7
--- /dev/null
+++ b/actions/devel/minio/nuvolaris/config.py
@@ -0,0 +1,202 @@
+# 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 flatdict, json, os
+import logging
+
+_config = {}
+
+# define a configuration
+# the configuration is a map, followed by a list of labels
+# the map can be a serialized json and will be flattened to a map of values.
+# you can have only a configuration active at a time
+# if you want to set a new configuration you have to clean it
+def configure(spec: dict):
+ global _config
+ _config = dict(flatdict.FlatDict(spec, delimiter="."))
+
+ #forces autodetect of kube if not provided
+ if not exists('nuvolaris.kube'):
+ put('nuvolaris.kube','auto')
+
+ return True
+
+def clean():
+ global _config
+ _config = {}
+
+def exists(key):
+ return key in _config
+
+def get(key, envvar=None, defval=None):
+ val = _config.get(key)
+
+ if envvar and envvar in os.environ:
+ val = os.environ[envvar]
+
+ if val:
+ return val
+
+ return defval
+
+def put(key, value):
+ _config[key] = value
+ return True
+
+def delete(key):
+ if key in _config:
+ del _config[key]
+ return True
+
+def getall(prefix=""):
+ res = {}
+ for key in _config.keys():
+ if key.startswith(prefix):
+ res[key] = _config[key]
+ return res
+
+def keys(prefix=""):
+ res = []
+ if _config:
+ for key in _config.keys():
+ if key.startswith(prefix):
+ res.append(key)
+ return res
+
+def detect_labels(labels=None):
+ # skip autodetection of nuvolaris.kube if already available in _config
+ if exists('nuvolaris.kube') and get('nuvolaris.kube') != 'auto':
+ logging.info(f"*** configuration provided already a nuvolaris.kube={get('nuvolaris.kube')}")
+ return {}
+
+ # read labels if not available
+ if not labels:
+ import nuvolaris.kube as kube
+ labels = kube.kubectl("get", "nodes", jsonpath='{.items[].metadata.labels}')
+
+ res = {}
+ kube = None
+ for i in labels:
+ for j in list(i.keys()):
+ if j.startswith("nuvolaris.io/"):
+ key = f"nuvolaris.{j[13:]}"
+ res[key] = i[j]
+ _config[key] = i[j]
+
+ for i in labels:
+ for j in list(i.keys()):
+ # detect the kube type
+ if j.find("eksctl.io") >= 0:
+ kube ="eks"
+ break
+ if j.find("k8s.io/cloud-provider-aws") >= 0:
+ kube ="eks"
+ break
+ elif j.find("microk8s.io") >= 0:
+ kube = "microk8s"
+ break
+ elif j.find("lke.linode.com") >=0:
+ kube = "lks"
+ break
+ elif j.find("node.openshift.io") >=0:
+ kube = "openshift"
+ break
+ elif j.endswith("kubernetes.io/instance-type") and i[j] == "k3s":
+ kube = "k3s"
+ break
+ elif j.find("cloud.google.com/gke") >=0:
+ kube = "gke"
+ break
+ elif j.find("kubernetes.azure.com") >=0:
+ kube = "aks"
+ break
+ # assign all the 'nuvolaris.io' labels
+
+ if kube:
+ res["nuvolaris.kube"] = kube
+ _config["nuvolaris.kube"] = kube
+ return res
+
+ # defaults to generic if it is not yet detected
+ if exists('nuvolaris.kube') and get('nuvolaris.kube') == 'auto':
+ _config["nuvolaris.kube"] = "generic"
+ res["nuvolaris.kube"] = "generic"
+
+ return res
+
+def detect_storage(storages=None):
+ res = {}
+ if not storages:
+ import nuvolaris.util as util
+
+ try:
+ detect_storageclass = True
+ detect_provisioner = True
+
+ # skips autodetection if already provided
+ if exists('nuvolaris.storageclass') and get('nuvolaris.storageclass') != 'auto':
+ logging.info(f"*** configuration provided already a nuvolaris.storageclass={get('nuvolaris.storageclass')}")
+ detect_storageclass = False
+
+ if exists('nuvolaris.provisioner') and get('nuvolaris.provisioner') != 'auto':
+ logging.info(f"*** configuration provided already a nuvolaris.provisioner={get('nuvolaris.provisioner')}")
+ detect_provisioner = False
+
+ if not detect_storageclass and not detect_provisioner:
+ return res
+
+ if detect_storageclass:
+ storage_class = util.get_default_storage_class()
+ if storage_class:
+ res['nuvolaris.storageclass'] = storage_class
+ _config['nuvolaris.storageclass'] = storage_class
+
+ if detect_provisioner:
+ provisioner = util.get_default_storage_provisioner()
+ if(provisioner):
+ res['nuvolaris.provisioner'] = provisioner
+ _config['nuvolaris.provisioner'] = provisioner
+ except:
+ pass
+ return res
+
+def detect_env():
+ _config['operator.image'] = os.environ.get("OPERATOR_IMAGE", "missing-OPERATOR_IMAGE")
+ _config['operator.tag'] = os.environ.get("OPERATOR_TAG", "missing-OPERATOR_TAG")
+
+ # skip autodetection of controller.image if already configure into whisk.yaml
+ if not exists('controller.image'):
+ _config['controller.image'] = os.environ.get("CONTROLLER_IMAGE", "missing-CONTROLLER_IMAGE")
+ _config['controller.tag'] = os.environ.get("CONTROLLER_TAG", "missing-CONTROLLER_TAG")
+ else:
+ logging.warn(f"OW controller image detection skipped. Using {get('controller.image')}")
+
+ if not exists('invoker.image'):
+ _config['invoker.image'] = os.environ.get("INVOKER_IMAGE", "missing-INVOKER_IMAGE")
+ _config['invoker.tag'] = os.environ.get("INVOKER_TAG", "missing-INVOKER_TAG")
+ else:
+ logging.warn(f"OW invoker image detection skipped. Using {get('invoker.image')}")
+
+def detect():
+ detect_storage()
+ detect_labels()
+ detect_env()
+
+def dump_config():
+ import nuvolaris.config as cfg
+ for k in cfg.getall():
+ logging.debug(f"{k} = {cfg.get(k)}")
diff --git a/actions/devel/minio/nuvolaris/couchdb_util.py b/actions/devel/minio/nuvolaris/couchdb_util.py
new file mode 100644
index 0000000..3adb6b9
--- /dev/null
+++ b/actions/devel/minio/nuvolaris/couchdb_util.py
@@ -0,0 +1,164 @@
+# 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 os, json, time, sys, logging
+import requests as req
+import nuvolaris.config as cfg
+
+class CouchDB:
+ def __init__(self):
+ self.db_protocol = "http"
+ self.db_prefix = "nuvolaris_"
+ self.db_port = cfg.get("couchdb.port", "COUCHDB_SERVICE_PORT", "5984")
+ self.db_host = cfg.get("couchdb.host", "COUCHDB_SERVICE_HOST", "couchdb")
+ self.db_username = cfg.get("couchdb.admin.user", "COUCHDB_ADMIN_USER", "whisk_admin")
+ self.db_password = cfg.get("couchdb.admin.password", "COUCHDB_ADMIN_PASSWORD", "some_passw0rd")
+ self.db_auth = req.auth.HTTPBasicAuth(self.db_username,self.db_password)
+ self.db_url = f"{self.db_protocol}://{self.db_host}:{self.db_port}"
+ self.db_base = f"{self.db_url}/{self.db_prefix}"
+ self.db_session = req.Session()
+ self.db_session.auth = self.db_auth
+
+ def wait_db_ready(self, max_seconds):
+ logging.info("entering CouchDB.wait_db_ready()")
+ start = time.time()
+ delta = 0
+ session = req.Session()
+ while delta < max_seconds:
+ try:
+ 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
+ except Exception as e:
+ logging.info(f"waiting since: {delta} seconds")
+ delta = int(time.time() - start)
+ time.sleep(1)
+ return False
+
+ # check if database exists, return boolean
+ def check_db(self, database):
+ url = f"{self.db_base}{database}"
+ r = self.db_session.head(url)
+ return r.status_code == 200
+
+ # delete database, return true if ok
+ def delete_db(self, database):
+ url = f"{self.db_base}{database}"
+ r = self.db_session.delete(url)
+ return r.status_code == 200
+
+ # create db, return true if ok
+ def create_db(self, database):
+ url = f"{self.db_base}{database}"
+ r = self.db_session.put(url)
+ return r.status_code == 201
+
+ # database="subjects"
+ def recreate_db(self, database, recreate=False):
+ msg = "recreate_db:"
+ exists = self.check_db(database)
+ if recreate and exists:
+ msg += " deleted"
+ self.delete_db(database)
+ if recreate or not exists:
+ msg += " created"
+ self.create_db(database)
+ return msg
+
+ def get_doc(self, database, id, user=None, password="", no_auth=False):
+ url = f"{self.db_base}{database}/{id}"
+ session = req.Session()
+ if no_auth:
+ db_auth=None
+ elif user:
+ db_auth=req.auth.HTTPBasicAuth(user, password)
+ else:
+ db_auth = self.db_auth
+ session.auth = db_auth
+ r = session.get(url)
+ if r.status_code == 200:
+ return json.loads(r.text)
+ return None
+
+ def update_doc(self, database, doc):
+ if '_id' in doc:
+ url = f"{self.db_base}{database}/{doc['_id']}"
+ cur = self.get_doc(database, doc['_id'])
+ if cur and '_rev' in cur:
+ doc['_rev'] = cur['_rev']
+ r = self.db_session.put(url, json=doc)
+ else:
+ r = self.db_session.put(url, json=doc)
+ return r.status_code in [200,201]
+ return False
+
+ def delete_doc(self, database, id):
+ cur = self.get_doc(database, id)
+ if cur and '_rev' in cur:
+ url = f"{self.db_base}{database}/{cur['_id']}?rev={cur['_rev']}"
+ r = self.db_session.delete(url)
+ return r.status_code == 200
+ return False
+
+ def configure_single_node(self):
+ url = f"{self.db_url}/_cluster_setup"
+ data = {"action": "enable_single_node", "singlenode": True, "bind_address": "0.0.0.0", "port": 5984}
+ r = self.db_session.post(url, json=data)
+ return r.status_code == 201
+
+ def configure_no_reduce_limit(self):
+ url = f"{self.db_url}/_node/_local/_config/query_server_config/reduce_limit"
+ data=b'"false"'
+ r = self.db_session.put(url, data=data)
+ return r.status_code == 200
+
+ def add_user(self, username: str, password: str):
+ userpass = {"name": username, "password": password, "roles": [], "type": "user"}
+ url = f"{self.db_url}/_users/org.couchdb.user:{username}"
+ res = self.db_session.put(url, json=userpass)
+ return res.status_code in [200, 201, 421]
+
+ #def add_role(self, database: str, members: list[str] = [], admins: list[str] =[]):
+ def add_role(self, database: str, members = [], admins =[]):
+ roles = {"admins": { "names": admins, "roles": [] }, "members": { "names": members, "roles": [] } }
+ url = f"{self.db_base}{database}/_security"
+ res = self.db_session.put(url, json=roles)
+ return res.status_code in [200, 201, 421]
+
+#
+# Submit a POST request to the _find endpoint using the specified selector
+#
+ def find_doc(self, database, selector, user=None, password="", no_auth=False):
+ url = f"{self.db_base}{database}/_find"
+ headers = {'Content-Type': 'application/json'}
+ session = req.Session()
+
+ if no_auth:
+ db_auth=None
+ elif user:
+ db_auth=req.auth.HTTPBasicAuth(user, password)
+ else:
+ db_auth = self.db_auth
+
+ session.auth=db_auth
+ r = session.post(url, headers=headers, data=selector)
+ if r.status_code == 200:
+ return json.loads(r.text)
+
+ logging.warn(f"query to {url} failed with {r.status_code}. Body {r.text}")
+ return None
\ No newline at end of file
diff --git a/actions/devel/psql/__main__.py b/actions/devel/psql/__main__.py
new file mode 100644
index 0000000..2ba865e
--- /dev/null
+++ b/actions/devel/psql/__main__.py
@@ -0,0 +1,74 @@
+# 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 nuvolaris.config as cfg
+import nuvolaris.couchdb_util as cu
+import logging, json
+
+from common.authorize import Authorize
+from common.command_data import CommandData
+from command.psql import Psql
+from base64 import b64decode
+
+class ApiError(Exception):
+ pass
+
+def build_error(message: str):
+ return {
+ "statusCode": 400,
+ "body": message
+ }
+
+def build_response(data:CommandData):
+ meta_data = data.get_metadata()
+ return {
+ "statusCode": meta_data['status'],
+ "body": meta_data['result']
+ }
+
+def parse_body(args):
+ try:
+ return b64decode(args['__ow_body']).decode().strip()
+ except Exception as e:
+ print(e)
+ raise ApiError("could not parse __ow_body as base64")
+
+def main(args):
+ """
+ Action implementing a generic command wrapper for the nuv devel plugin. The action must be called with a POST request receiving a JSON
+ payload similar to this one
+ {
+ "command":"select * from table"
+ }
+
+ the invoker must provide a x-impersonate-auth header containing the Openwhisk BASIC authentication of the wsku/user the action should impersonate
+ when submitting the command. Every specific provider will support specific request type and command.
+ WARNING: the body will be received as base64 encoded string as this action will be deployed with --web raw enabled flag
+ """
+ headers = args['__ow_headers']
+ if('x-impersonate-auth' not in headers):
+ return build_error("invalid request, missing mandatory header: x-impersonate-auth")
+
+ if(len(args['__ow_body']) == 0):
+ return build_error("invalid request, no command payload received")
+
+ try:
+ user_data = Authorize(args['couchdb_host'],args['couchdb_user'],args['couchdb_password']).login(headers['x-impersonate-auth'])
+ cmd = CommandData(json.loads(parse_body(args)))
+ return build_response(Psql(user_data).execute(cmd))
+ except Exception as e:
+ return build_error(f"failed to execute nuv devel psql. Reason: {str(e)}")
\ No newline at end of file
diff --git a/actions/devel/psql/command/psql.py b/actions/devel/psql/command/psql.py
new file mode 100644
index 0000000..8067802
--- /dev/null
+++ b/actions/devel/psql/command/psql.py
@@ -0,0 +1,84 @@
+# 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 psycopg
+import common.util as ut
+
+from common.command_data import CommandData
+from psycopg.rows import dict_row
+
+class Psql():
+ """
+ Implementation of a Postgres Command executor. It will require
+ a user_data dictionary linked to a specific user.
+ """
+
+ def __init__(self, user_data):
+ self._user_data = user_data
+ self._postgres_url= ut.get_env_value(user_data,"POSTGRES_URL")
+ self.validate()
+
+ def validate(self):
+ """
+ Validate that the provided user_data contains the appropriate
+ metadata for being able to submit a postgres command.
+ """
+ if not self._postgres_url:
+ raise Exception("user does not have valid POSTGRES environment set")
+
+
+ def _query(self, input:CommandData):
+ """
+ Queries for matching query records and returns datas in a key, value format.
+ """
+ query = input.command()
+ with psycopg.connect(self._postgres_url) as conn:
+ # Open a cursor to perform database operations
+ with conn.cursor(row_factory=dict_row) as cur:
+ cur.execute(query)
+ result = cur.fetchall()
+ input.result(str(result))
+ input.status(200)
+ return input
+
+ def _script(self, input:CommandData):
+ script = input.command()
+ with psycopg.connect(self._postgres_url) as conn:
+ # Open a cursor to perform database operations
+ with conn.cursor() as cur:
+ cur.execute(script)
+ conn.commit()
+ input.result(cur.statusmessage)
+ input.status(200)
+ return input
+
+ def _is_a_query(self, input:CommandData):
+ return 'select' in input.command().lower()
+
+ def execute(self, input:CommandData):
+ print(f"**** Psql command to execute {input.command()}")
+ try:
+ if self._is_a_query(input):
+ return self._query(input)
+ else:
+ return self._script(input)
+ except Exception as e:
+ input.result(f"could not execute psql command {e}")
+ input.status(400)
+
+ return input
\ No newline at end of file
diff --git a/actions/devel/psql/common/authorize.py b/actions/devel/psql/common/authorize.py
new file mode 100644
index 0000000..f7d716f
--- /dev/null
+++ b/actions/devel/psql/common/authorize.py
@@ -0,0 +1,162 @@
+# 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.
+#
+from base64 import b64decode, b64encode
+from urllib.parse import quote, unquote
+
+import nuvolaris.config as cfg
+import nuvolaris.couchdb_util as cu
+import json
+import common.util as ut
+
+USER_META_DBN = "users_metadata"
+SUBJECT_META_DBN = "subjects"
+
+class DecodeError(Exception):
+ pass
+
+class EncodeError(Exception):
+ pass
+
+class AuthorizationError(Exception):
+ pass
+
+class Authorize():
+
+ def __init__(self, cdb_host, cdb_user, cdb_pwd):
+ cfg.clean()
+ cfg.put("couchdb.host", cdb_host)
+ cfg.put("couchdb.admin.user", cdb_user)
+ cfg.put("couchdb.admin.password", cdb_pwd)
+ self._db = cu.CouchDB()
+
+ def encode(self,username, password):
+ """Returns an HTTP basic authentication encrypted string given a valid
+ username and password.
+ """
+ if ':' in username:
+ raise EncodeError
+
+ username_password = f'{quote(username)}:{quote(password)}'
+ return f'Basic {b64encode(username_password.encode()).decode()}'
+
+ def _parse_b64(self, encoded_str):
+ try:
+ username, password = b64decode(encoded_str).decode().split(':', 1)
+ except:
+ # fallback in case the token is not bas64 encoded
+ username, password = encoded_str.split(':', 1)
+
+ if not username or not password:
+ raise DecodeError("authentication token does not seems to be b64 encoded")
+
+ return username, password
+
+ def decode(self, encoded_str):
+ """Decode an encrypted HTTP basic authentication string. Returns a tuple of
+ the form (username, password), and raises a DecodeError exception if
+ nothing could be decoded.
+ """
+ split = encoded_str.strip().split(' ')
+
+ # If split is only one element, try to decode the username and password
+ # directly.
+ if len(split) == 1:
+ username, password = self._parse_b64(split[0])
+
+ # If there are only two elements, check the first and ensure it says
+ # 'basic' so that we know we're about to decode the right thing. If not,
+ # bail out.
+ elif len(split) == 2:
+ if split[0].strip().lower() == 'basic':
+ username, password = self._parse_b64(split[1])
+ else:
+ raise DecodeError("authentication token provides more than 2 elements. could not parse properly")
+
+ # If there are more than 2 elements, something crazy must be happening.
+ # Bail.
+ else:
+ raise DecodeError("unpredictable error parsing authentication token")
+
+ return unquote(username), unquote(password)
+
+ def fetch_subject(self, uuid: str, key: str):
+ """
+ Query the internal couchdb searching for the subject mathing the given uuid, key.
+ Normally these stored in wsk or wsku in the form uuid:key
+ :param uuid the OW subject uuid
+ :param key the OW subject key
+ :return a ubject document
+ """
+ print(f"searching for openwhisk subject {uuid}")
+ try:
+ selector= {
+ "selector": {"namespaces": {"$elemMatch": {"uuid": uuid,"key": key}}}
+ }
+
+ response = self._db.find_doc(SUBJECT_META_DBN, json.dumps(selector))
+
+ if(response['docs']):
+ docs = list(response['docs'])
+ if(len(docs) > 0):
+ print(f"Nuvolaris namespace for user {uuid} found. Returning Result.")
+ return docs[0]
+
+ print(f"Nuvolaris metadata for user {uuid} not found!")
+ return None
+ except Exception as e:
+ print(f"failed to query Nuvolaris metadata for user {uuid}. Reason: {e}")
+ return None
+
+ def fetch_user_data(self, username: str):
+ """
+ Query the internal couchdb searching for the given principal to retrieve all the
+ relevant metadata
+ """
+ print(f"searching for user {username} meta-data")
+ try:
+ selector = {"selector":{"login": {"$eq": username }}}
+ response = self._db.find_doc(USER_META_DBN, json.dumps(selector))
+
+ if(response['docs']):
+ docs = list(response['docs'])
+ if(len(docs) > 0):
+ print(f"Nuvolaris metadata for user {username} found. Returning Result.")
+ return docs[0]
+
+ print(f"Nuvolaris metadata for user {username} not found!")
+ return None
+ except Exception as e:
+ print(f"failed to query Nuvolaris metadata for user {username}. Reason: {e}")
+ return None
+
+ def login(self, authorization: str):
+ """
+ Attempt to login the user identified by the given Openwhisk authorization AUTH token as base64
+ """
+ uuid, key = self.decode(authorization)
+ subject = self.fetch_subject(uuid, key)
+
+ if not subject:
+ raise AuthorizationError("Openwhisk subject not found.")
+
+ user_data = self.fetch_user_data(subject['subject'])
+
+ if user_data:
+ return user_data
+
+ raise AuthorizationError("Could not retrieve user metadata.")
\ No newline at end of file
diff --git a/actions/devel/psql/common/command_data.py b/actions/devel/psql/common/command_data.py
new file mode 100644
index 0000000..44f3faa
--- /dev/null
+++ b/actions/devel/psql/common/command_data.py
@@ -0,0 +1,50 @@
+# 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 json
+import logging
+
+class CommandData:
+ _data = {}
+ _raw_data = {}
+
+ def __init__(self, cmd):
+ self._data = cmd
+ self._raw_data = cmd.copy()
+
+ def dump(self):
+ logging.debug(json.dumps(self._data))
+
+ def status(self, status):
+ self._data['status']=status
+
+ def result(self, result):
+ self._data['result']=result
+
+ def get_metadata(self):
+ return self._data
+
+ def get_raw_data(self):
+ return self._raw_data
+
+ def command(self):
+ return self._data['command']
+
+ def args(self):
+ return self._data['args']
+
+
diff --git a/actions/devel/psql/common/minio_util.py b/actions/devel/psql/common/minio_util.py
new file mode 100644
index 0000000..e1e8f90
--- /dev/null
+++ b/actions/devel/psql/common/minio_util.py
@@ -0,0 +1,182 @@
+# 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 os
+import base64
+import random
+import string
+import mimetypes
+
+from minio import Minio
+from minio.error import S3Error
+from minio.commonconfig import CopySource
+
+def extract_mimetype(file):
+ mimetype, _ = mimetypes.guess_type(file)
+
+ if mimetype is None:
+ return "application/octet-stream"
+ else:
+ return mimetype
+
+def build_mo_client(host, port, access_key, secret_key):
+ """
+ Creates an Minio client pointing to the given MINIO HOST
+ :param host, minio host
+ :param port, minio api port normally it is the 9000
+ :param access_key user we are representing
+ :param secret_key to access minio
+ """
+ mo_client = Minio(f"{host}:{port}",access_key=access_key,secret_key=secret_key,secure=False)
+ return mo_client
+
+def upload_file(mo_client, file, bucket, object_name=None):
+ """Upload a file to an S3 bucket
+
+ :param file_name: File to upload
+ :param bucket: Bucket to upload to
+ :param object_name: S3 object name. If not specified then file_name is used
+ :return: a tuple with the Booleand str,
+ """
+
+ # If S3 object_name was not specified, use file_name
+ if object_name is None:
+ object_name = os.path.basename(file)
+
+ print(f"uploading {object_name} into bucket {bucket} from tmp_file {file}")
+
+ # Upload the file
+ try:
+ mimetype = extract_mimetype(object_name)
+ response = mo_client.fput_object(bucket_name=bucket,object_name=object_name,file_path=file,content_type=mimetype)
+ if response._object_name:
+ return True, response._object_name
+ except Exception as e:
+ print(e)
+ return False, str(e)
+ return False, "KO"
+
+def prepare_file_upload(username, filename, file_content_as_b64):
+ """ Creates a tmp area for the given user where the uploaded file will be stored under a random generated name
+ the fully qualified filename is taken into account only on the corresponding destination bucket.
+ param: username
+ param: filename
+ param: file_content_as_b64
+ return: a file object pointing to the tmp file
+ """
+ try:
+ user_tmp_folder = f"/tmp/{username}"
+ if not os.path.exists(user_tmp_folder):
+ os.makedirs(user_tmp_folder)
+ print(f"added tmp folder {user_tmp_folder}")
+
+ delete_files_in_directory_and_subdirectories(user_tmp_folder)
+ rnd_filename = get_random_string(20)
+ tmp_file = f"{user_tmp_folder}/{rnd_filename}"
+
+ with open(tmp_file, "wb") as f:
+ file_content=base64.b64decode(file_content_as_b64)
+ f.write(file_content)
+
+ if os.path.exists(tmp_file):
+ print(f"{tmp_file} stored successfully.")
+ return tmp_file
+ else:
+ return None
+ except Exception as e:
+ print("error preparing tmp_files",e)
+ return None
+
+def delete_files_in_directory_and_subdirectories(directory_path):
+ try:
+ for root, dirs, files in os.walk(directory_path):
+ for file in files:
+ file_path = os.path.join(root, file)
+ os.remove(file_path)
+ print("All files and subdirectories deleted successfully.")
+ except OSError:
+ print("Error occurred while deleting files and subdirectories.")
+
+def get_random_string(length):
+ # choose from all lowercase letter
+ letters = string.ascii_lowercase
+ return ''.join(random.choice(letters) for i in range(length))
+
+def rm_file(mo_client, bucket, file):
+ """ Remove a file from a bucket
+ :parma mo_client a minio client instance to execute the command
+ :param bucket the bucket name
+ :param file the file name
+ :return True if the file has been removed, False otherwise
+ """
+
+ try:
+ mo_client.remove_object(bucket, file)
+ return True
+ except Exception as e:
+ print(e)
+ return False
+ return False
+
+def cp_file(mo_client, orig_bucket, orig_file, dest_bucket, dest_file):
+ """Copy a file between two buckets
+
+ :param orig_bucket: Origin bucket
+ :param orig_file: Origin file
+ :param dest_bucket: Destination bucket
+ :param dest_file: Destination file
+ :return: name of the destination file is everything is ok. None otherwise
+ """
+ print(f"**** moving file {orig_file} from bucket {orig_bucket} to bucket {dest_bucket} with name {dest_file}")
+ try:
+ # copy an object from a bucket to another.
+ cp_result = mo_client.copy_object(
+ dest_bucket,
+ dest_file,
+ CopySource(orig_bucket, orig_file)
+ )
+ if cp_result.object_name:
+ return cp_result.object_name
+ except Exception as e:
+ print(e)
+ return None
+ return None
+
+def mv_file(mo_client, orig_bucket, orig_file, dest_bucket, dest_file):
+ """Move a file between two buckets
+
+ :param orig_bucket: Origin bucket
+ :param orig_file: Origin file
+ :param dest_bucket: Destination bucket
+ :param dest_file: Destination file
+ :return: name of the destination file is everything is ok. None otherwise
+ """
+ print(f"**** moving file {orig_file} from bucket {orig_bucket} to bucket {dest_bucket} with name {dest_file}")
+
+ # Upload the file
+ try:
+ # mv an object from a bucket to another.
+ object_name = cp_file(mo_client, orig_bucket, orig_file, dest_bucket, dest_file)
+ if object_name:
+ rm_file(mo_client,orig_bucket,orig_file)
+ return object_name
+ except Exception as e:
+ print(e)
+ return None
+ return None
+
diff --git a/actions/devel/psql/common/util.py b/actions/devel/psql/common/util.py
new file mode 100644
index 0000000..f1e8f39
--- /dev/null
+++ b/actions/devel/psql/common/util.py
@@ -0,0 +1,35 @@
+# 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 mimetypes
+
+def get_env_value(user_data, key):
+ """
+ Check if inside the given user_data object there is an env item with the
+ given name and returns the value if any.
+ :param user_data
+ :param key
+ :return None if the given key it is not prese
+ """
+ envs = list(user_data['env'])
+
+ for env in envs:
+ if env['key'] == key:
+ return env['value']
+
+ return None
\ No newline at end of file
diff --git a/actions/devel/psql/flatdict.py b/actions/devel/psql/flatdict.py
new file mode 100644
index 0000000..834ce99
--- /dev/null
+++ b/actions/devel/psql/flatdict.py
@@ -0,0 +1,506 @@
+# 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.
+
+"""FlatDict is a dict object that allows for single level, delimited
+key/value pair mapping of nested dictionaries.
+
+"""
+try:
+ from collections.abc import MutableMapping
+except ImportError: # pragma: nocover
+ from collections import MutableMapping
+import sys
+
+__version__ = '4.0.1'
+
+NO_DEFAULT = object()
+
+
+class FlatDict(MutableMapping):
+ """:class:`~flatdict.FlatDict` is a dictionary object that allows for
+ single level, delimited key/value pair mapping of nested dictionaries.
+ The default delimiter value is ``:`` but can be changed in the constructor
+ or by calling :meth:`FlatDict.set_delimiter`.
+
+ """
+ _COERCE = dict
+
+ def __init__(self, value=None, delimiter=':', dict_class=dict):
+ super(FlatDict, self).__init__()
+ self._values = dict_class()
+ self._delimiter = delimiter
+ self.update(value)
+
+ def __contains__(self, key):
+ """Check to see if the key exists, checking for both delimited and
+ not delimited key values.
+
+ :param mixed key: The key to check for
+
+ """
+ if self._has_delimiter(key):
+ pk, ck = key.split(self._delimiter, 1)
+ return pk in self._values and ck in self._values[pk]
+ return key in self._values
+
+ def __delitem__(self, key):
+ """Delete the item for the specified key, automatically dealing with
+ nested children.
+
+ :param mixed key: The key to use
+ :raises: KeyError
+
+ """
+ if key not in self:
+ raise KeyError
+ if self._has_delimiter(key):
+ pk, ck = key.split(self._delimiter, 1)
+ del self._values[pk][ck]
+ if not self._values[pk]:
+ del self._values[pk]
+ else:
+ del self._values[key]
+
+ def __eq__(self, other):
+ """Check for equality against the other value
+
+ :param other: The value to compare
+ :type other: FlatDict
+ :rtype: bool
+ :raises: TypeError
+
+ """
+ if isinstance(other, dict):
+ return self.as_dict() == other
+ elif not isinstance(other, self.__class__):
+ raise TypeError
+ return self.as_dict() == other.as_dict()
+
+ def __ne__(self, other):
+ """Check for inequality against the other value
+
+ :param other: The value to compare
+ :type other: dict or FlatDict
+ :rtype: bool
+
+ """
+ return not self.__eq__(other)
+
+ def __getitem__(self, key):
+ """Get an item for the specified key, automatically dealing with
+ nested children.
+
+ :param mixed key: The key to use
+ :rtype: mixed
+ :raises: KeyError
+
+ """
+ values = self._values
+ key = [key] if isinstance(key, int) else key.split(self._delimiter)
+ for part in key:
+ values = values[part]
+ return values
+
+ def __iter__(self):
+ """Iterate over the flat dictionary key and values
+
+ :rtype: Iterator
+ :raises: RuntimeError
+
+ """
+ return iter(self.keys())
+
+ def __len__(self):
+ """Return the number of items.
+
+ :rtype: int
+
+ """
+ return len(self.keys())
+
+ def __reduce__(self):
+ """Return state information for pickling
+
+ :rtype: tuple
+
+ """
+ return type(self), (self.as_dict(), self._delimiter)
+
+ def __repr__(self):
+ """Return the string representation of the instance.
+
+ :rtype: str
+
+ """
+ return '<{} id={} {}>"'.format(self.__class__.__name__, id(self),
+ str(self))
+
+ def __setitem__(self, key, value):
+ """Assign the value to the key, dynamically building nested
+ FlatDict items where appropriate.
+
+ :param mixed key: The key for the item
+ :param mixed value: The value for the item
+ :raises: TypeError
+
+ """
+ if isinstance(value, self._COERCE) and not isinstance(value, FlatDict):
+ value = self.__class__(value, self._delimiter)
+ if self._has_delimiter(key):
+ pk, ck = key.split(self._delimiter, 1)
+ if pk not in self._values:
+ self._values[pk] = self.__class__({ck: value}, self._delimiter)
+ return
+ elif not isinstance(self._values[pk], FlatDict):
+ raise TypeError(
+ 'Assignment to invalid type for key {}'.format(pk))
+ self._values[pk][ck] = value
+ else:
+ self._values[key] = value
+
+ def __str__(self):
+ """Return the string value of the instance.
+
+ :rtype: str
+
+ """
+ return '{{{}}}'.format(', '.join(
+ ['{!r}: {!r}'.format(k, self[k]) for k in self.keys()]))
+
+ def as_dict(self):
+ """Return the :class:`~flatdict.FlatDict` as a :class:`dict`
+
+ :rtype: dict
+
+ """
+ out = dict({})
+ for key in self.keys():
+ if self._has_delimiter(key):
+ pk, ck = key.split(self._delimiter, 1)
+ if self._has_delimiter(ck):
+ ck = ck.split(self._delimiter, 1)[0]
+ if isinstance(self._values[pk], FlatDict) and pk not in out:
+ out[pk] = {}
+ if isinstance(self._values[pk][ck], FlatDict):
+ out[pk][ck] = self._values[pk][ck].as_dict()
+ else:
+ out[pk][ck] = self._values[pk][ck]
+ else:
+ out[key] = self._values[key]
+ return out
+
+ def clear(self):
+ """Remove all items from the flat dictionary."""
+ self._values.clear()
+
+ def copy(self):
+ """Return a shallow copy of the flat dictionary.
+
+ :rtype: flatdict.FlatDict
+
+ """
+ return self.__class__(self.as_dict(), delimiter=self._delimiter)
+
+ def get(self, key, d=None):
+ """Return the value for key if key is in the flat dictionary, else
+ default. If default is not given, it defaults to ``None``, so that this
+ method never raises :exc:`KeyError`.
+
+ :param mixed key: The key to get
+ :param mixed d: The default value
+ :rtype: mixed
+
+ """
+ try:
+ return self.__getitem__(key)
+ except KeyError:
+ return d
+
+ def items(self):
+ """Return a copy of the flat dictionary's list of ``(key, value)``
+ pairs.
+
+ .. note:: CPython implementation detail: Keys and values are listed in
+ an arbitrary order which is non-random, varies across Python
+ implementations, and depends on the flat dictionary's history of
+ insertions and deletions.
+
+ :rtype: list
+
+ """
+ return [(k, self.__getitem__(k)) for k in self.keys()]
+
+ def iteritems(self):
+ """Return an iterator over the flat dictionary's (key, value) pairs.
+ See the note for :meth:`flatdict.FlatDict.items`.
+
+ Using ``iteritems()`` while adding or deleting entries in the flat
+ dictionary may raise :exc:`RuntimeError` or fail to iterate over all
+ entries.
+
+ :rtype: Iterator
+ :raises: RuntimeError
+
+ """
+ for item in self.items():
+ yield item
+
+ def iterkeys(self):
+ """Iterate over the flat dictionary's keys. See the note for
+ :meth:`flatdict.FlatDict.items`.
+
+ Using ``iterkeys()`` while adding or deleting entries in the flat
+ dictionary may raise :exc:`RuntimeError` or fail to iterate over all
+ entries.
+
+ :rtype: Iterator
+ :raises: RuntimeError
+
+ """
+ for key in self.keys():
+ yield key
+
+ def itervalues(self):
+ """Return an iterator over the flat dictionary's values. See the note
+ :meth:`flatdict.FlatDict.items`.
+
+ Using ``itervalues()`` while adding or deleting entries in the flat
+ dictionary may raise a :exc:`RuntimeError` or fail to iterate over all
+ entries.
+
+ :rtype: Iterator
+ :raises: RuntimeError
+
+ """
+ for value in self.values():
+ yield value
+
+ def keys(self):
+ """Return a copy of the flat dictionary's list of keys.
+ See the note for :meth:`flatdict.FlatDict.items`.
+
+ :rtype: list
+
+ """
+ keys = []
+
+ for key, value in self._values.items():
+ if isinstance(value, (FlatDict, dict)):
+ nested = [
+ self._delimiter.join([str(key), str(k)])
+ for k in value.keys()]
+ keys += nested if nested else [key]
+ else:
+ keys.append(key)
+
+ return keys
+
+ def pop(self, key, default=NO_DEFAULT):
+ """If key is in the flat dictionary, remove it and return its value,
+ else return default. If default is not given and key is not in the
+ dictionary, :exc:`KeyError` is raised.
+
+ :param mixed key: The key name
+ :param mixed default: The default value
+ :rtype: mixed
+
+ """
+ if key not in self and default != NO_DEFAULT:
+ return default
+ value = self[key]
+ self.__delitem__(key)
+ return value
+
+ def setdefault(self, key, default):
+ """If key is in the flat dictionary, return its value. If not,
+ insert key with a value of default and return default.
+ default defaults to ``None``.
+
+ :param mixed key: The key name
+ :param mixed default: The default value
+ :rtype: mixed
+
+ """
+ if key not in self:
+ self.__setitem__(key, default)
+ return self.__getitem__(key)
+
+ def set_delimiter(self, delimiter):
+ """Override the default or passed in delimiter with a new value. If
+ the requested delimiter already exists in a key, a :exc:`ValueError`
+ will be raised.
+
+ :param str delimiter: The delimiter to use
+ :raises: ValueError
+
+ """
+ for key in self.keys():
+ if delimiter in key:
+ raise ValueError('Key {!r} collides with delimiter {!r}', key,
+ delimiter)
+ self._delimiter = delimiter
+ for key in self._values.keys():
+ if isinstance(self._values[key], FlatDict):
+ self._values[key].set_delimiter(delimiter)
+
+ def update(self, other=None, **kwargs):
+ """Update the flat dictionary with the key/value pairs from other,
+ overwriting existing keys.
+
+ ``update()`` accepts either another flat dictionary object or an
+ iterable of key/value pairs (as tuples or other iterables of length
+ two). If keyword arguments are specified, the flat dictionary is then
+ updated with those key/value pairs: ``d.update(red=1, blue=2)``.
+
+ :param iterable other: Iterable of key, value pairs
+ :rtype: None
+
+ """
+ [self.__setitem__(k, v) for k, v in dict(other or kwargs).items()]
+
+ def values(self):
+ """Return a copy of the flat dictionary's list of values. See the note
+ for :meth:`flatdict.FlatDict.items`.
+
+ :rtype: list
+
+ """
+ return [self.__getitem__(k) for k in self.keys()]
+
+ def _has_delimiter(self, key):
+ """Checks to see if the key contains the delimiter.
+
+ :rtype: bool
+
+ """
+ return isinstance(key, str) and self._delimiter in key
+
+
+class FlatterDict(FlatDict):
+ """Like :class:`~flatdict.FlatDict` but also coerces lists and sets
+ to child-dict instances with the offset as the key. Alternative to
+ the implementation added in v1.2 of FlatDict.
+
+ """
+ _COERCE = list, tuple, set, dict, FlatDict
+ _ARRAYS = list, set, tuple
+
+ def __init__(self, value=None, delimiter=':', dict_class=dict):
+ self.original_type = type(value)
+ if self.original_type in self._ARRAYS:
+ value = {str(i): v for i, v in enumerate(value)}
+ super(FlatterDict, self).__init__(value, delimiter, dict_class)
+
+ def __setitem__(self, key, value):
+ """Assign the value to the key, dynamically building nested
+ FlatDict items where appropriate.
+
+ :param mixed key: The key for the item
+ :param mixed value: The value for the item
+ :raises: TypeError
+
+ """
+ if isinstance(value, self._COERCE) and \
+ not isinstance(value, FlatterDict):
+ value = self.__class__(value, self._delimiter)
+ if self._has_delimiter(key):
+ pk, ck = key.split(self._delimiter, 1)
+ if pk not in self._values:
+ self._values[pk] = self.__class__({ck: value}, self._delimiter)
+ return
+ if getattr(self._values[pk], 'original_type',
+ None) in self._ARRAYS:
+ try:
+ k, cck = ck.split(self._delimiter, 1)
+ int(k)
+ except ValueError:
+ raise TypeError(
+ 'Assignment to invalid type for key {}{}{}'.format(
+ pk, self._delimiter, ck))
+ self._values[pk][k][cck] = value
+ return
+ elif not isinstance(self._values[pk], FlatterDict):
+ raise TypeError(
+ 'Assignment to invalid type for key {}'.format(pk))
+ self._values[pk][ck] = value
+ else:
+ self._values[key] = value
+
+ def as_dict(self):
+ """Return the :class:`~flatdict.FlatterDict` as a nested
+ :class:`dict`.
+
+ :rtype: dict
+
+ """
+ out = {}
+ for key in self.keys():
+ if self._has_delimiter(key):
+ pk, ck = key.split(self._delimiter, 1)
+ if self._has_delimiter(ck):
+ ck = ck.split(self._delimiter, 1)[0]
+ if isinstance(self._values[pk], FlatterDict) and pk not in out:
+ if self._values[pk].original_type == tuple:
+ out[pk] = tuple(self._child_as_list(pk))
+ elif self._values[pk].original_type == list:
+ out[pk] = self._child_as_list(pk)
+ elif self._values[pk].original_type == set:
+ out[pk] = set(self._child_as_list(pk))
+ elif self._values[pk].original_type == dict:
+ out[pk] = self._values[pk].as_dict()
+ else:
+ if isinstance(self._values[key], FlatterDict):
+ out[key] = self._values[key].original_type()
+ else:
+ out[key] = self._values[key]
+ return out
+
+ def _child_as_list(self, pk, ck=None):
+ """Returns a list of values from the child FlatterDict instance
+ with string based integer keys.
+
+ :param str pk: The parent key
+ :param str ck: The child key, optional
+ :rtype: list
+
+ """
+ if ck is None:
+ subset = self._values[pk]
+ else:
+ subset = self._values[pk][ck]
+ # Check if keys has delimiter, which implies deeply nested dict
+ keys = subset.keys()
+ if any(self._has_delimiter(k) for k in keys):
+ out = []
+ split_keys = {k.split(self._delimiter)[0] for k in keys}
+ for k in sorted(split_keys, key=lambda x: int(x)):
+ if subset[k].original_type == tuple:
+ out.append(tuple(self._child_as_list(pk, k)))
+ elif subset[k].original_type == list:
+ out.append(self._child_as_list(pk, k))
+ elif subset[k].original_type == set:
+ out.append(set(self._child_as_list(pk, k)))
+ elif subset[k].original_type == dict:
+ out.append(subset[k].as_dict())
+ return out
+
+ # Python prior 3.6 does not guarantee insertion order, remove it after
+ # EOL python 3.5 - 2020-09-13
+ if sys.version_info[0:2] < (3, 6): # pragma: nocover
+ return [subset[k] for k in sorted(keys, key=lambda x: int(x))]
+ else:
+ return [subset[k] for k in keys]
diff --git a/actions/devel/psql/nuvolaris/config.py b/actions/devel/psql/nuvolaris/config.py
new file mode 100644
index 0000000..787d4a7
--- /dev/null
+++ b/actions/devel/psql/nuvolaris/config.py
@@ -0,0 +1,202 @@
+# 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 flatdict, json, os
+import logging
+
+_config = {}
+
+# define a configuration
+# the configuration is a map, followed by a list of labels
+# the map can be a serialized json and will be flattened to a map of values.
+# you can have only a configuration active at a time
+# if you want to set a new configuration you have to clean it
+def configure(spec: dict):
+ global _config
+ _config = dict(flatdict.FlatDict(spec, delimiter="."))
+
+ #forces autodetect of kube if not provided
+ if not exists('nuvolaris.kube'):
+ put('nuvolaris.kube','auto')
+
+ return True
+
+def clean():
+ global _config
+ _config = {}
+
+def exists(key):
+ return key in _config
+
+def get(key, envvar=None, defval=None):
+ val = _config.get(key)
+
+ if envvar and envvar in os.environ:
+ val = os.environ[envvar]
+
+ if val:
+ return val
+
+ return defval
+
+def put(key, value):
+ _config[key] = value
+ return True
+
+def delete(key):
+ if key in _config:
+ del _config[key]
+ return True
+
+def getall(prefix=""):
+ res = {}
+ for key in _config.keys():
+ if key.startswith(prefix):
+ res[key] = _config[key]
+ return res
+
+def keys(prefix=""):
+ res = []
+ if _config:
+ for key in _config.keys():
+ if key.startswith(prefix):
+ res.append(key)
+ return res
+
+def detect_labels(labels=None):
+ # skip autodetection of nuvolaris.kube if already available in _config
+ if exists('nuvolaris.kube') and get('nuvolaris.kube') != 'auto':
+ logging.info(f"*** configuration provided already a nuvolaris.kube={get('nuvolaris.kube')}")
+ return {}
+
+ # read labels if not available
+ if not labels:
+ import nuvolaris.kube as kube
+ labels = kube.kubectl("get", "nodes", jsonpath='{.items[].metadata.labels}')
+
+ res = {}
+ kube = None
+ for i in labels:
+ for j in list(i.keys()):
+ if j.startswith("nuvolaris.io/"):
+ key = f"nuvolaris.{j[13:]}"
+ res[key] = i[j]
+ _config[key] = i[j]
+
+ for i in labels:
+ for j in list(i.keys()):
+ # detect the kube type
+ if j.find("eksctl.io") >= 0:
+ kube ="eks"
+ break
+ if j.find("k8s.io/cloud-provider-aws") >= 0:
+ kube ="eks"
+ break
+ elif j.find("microk8s.io") >= 0:
+ kube = "microk8s"
+ break
+ elif j.find("lke.linode.com") >=0:
+ kube = "lks"
+ break
+ elif j.find("node.openshift.io") >=0:
+ kube = "openshift"
+ break
+ elif j.endswith("kubernetes.io/instance-type") and i[j] == "k3s":
+ kube = "k3s"
+ break
+ elif j.find("cloud.google.com/gke") >=0:
+ kube = "gke"
+ break
+ elif j.find("kubernetes.azure.com") >=0:
+ kube = "aks"
+ break
+ # assign all the 'nuvolaris.io' labels
+
+ if kube:
+ res["nuvolaris.kube"] = kube
+ _config["nuvolaris.kube"] = kube
+ return res
+
+ # defaults to generic if it is not yet detected
+ if exists('nuvolaris.kube') and get('nuvolaris.kube') == 'auto':
+ _config["nuvolaris.kube"] = "generic"
+ res["nuvolaris.kube"] = "generic"
+
+ return res
+
+def detect_storage(storages=None):
+ res = {}
+ if not storages:
+ import nuvolaris.util as util
+
+ try:
+ detect_storageclass = True
+ detect_provisioner = True
+
+ # skips autodetection if already provided
+ if exists('nuvolaris.storageclass') and get('nuvolaris.storageclass') != 'auto':
+ logging.info(f"*** configuration provided already a nuvolaris.storageclass={get('nuvolaris.storageclass')}")
+ detect_storageclass = False
+
+ if exists('nuvolaris.provisioner') and get('nuvolaris.provisioner') != 'auto':
+ logging.info(f"*** configuration provided already a nuvolaris.provisioner={get('nuvolaris.provisioner')}")
+ detect_provisioner = False
+
+ if not detect_storageclass and not detect_provisioner:
+ return res
+
+ if detect_storageclass:
+ storage_class = util.get_default_storage_class()
+ if storage_class:
+ res['nuvolaris.storageclass'] = storage_class
+ _config['nuvolaris.storageclass'] = storage_class
+
+ if detect_provisioner:
+ provisioner = util.get_default_storage_provisioner()
+ if(provisioner):
+ res['nuvolaris.provisioner'] = provisioner
+ _config['nuvolaris.provisioner'] = provisioner
+ except:
+ pass
+ return res
+
+def detect_env():
+ _config['operator.image'] = os.environ.get("OPERATOR_IMAGE", "missing-OPERATOR_IMAGE")
+ _config['operator.tag'] = os.environ.get("OPERATOR_TAG", "missing-OPERATOR_TAG")
+
+ # skip autodetection of controller.image if already configure into whisk.yaml
+ if not exists('controller.image'):
+ _config['controller.image'] = os.environ.get("CONTROLLER_IMAGE", "missing-CONTROLLER_IMAGE")
+ _config['controller.tag'] = os.environ.get("CONTROLLER_TAG", "missing-CONTROLLER_TAG")
+ else:
+ logging.warn(f"OW controller image detection skipped. Using {get('controller.image')}")
+
+ if not exists('invoker.image'):
+ _config['invoker.image'] = os.environ.get("INVOKER_IMAGE", "missing-INVOKER_IMAGE")
+ _config['invoker.tag'] = os.environ.get("INVOKER_TAG", "missing-INVOKER_TAG")
+ else:
+ logging.warn(f"OW invoker image detection skipped. Using {get('invoker.image')}")
+
+def detect():
+ detect_storage()
+ detect_labels()
+ detect_env()
+
+def dump_config():
+ import nuvolaris.config as cfg
+ for k in cfg.getall():
+ logging.debug(f"{k} = {cfg.get(k)}")
diff --git a/actions/devel/psql/nuvolaris/couchdb_util.py b/actions/devel/psql/nuvolaris/couchdb_util.py
new file mode 100644
index 0000000..3adb6b9
--- /dev/null
+++ b/actions/devel/psql/nuvolaris/couchdb_util.py
@@ -0,0 +1,164 @@
+# 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 os, json, time, sys, logging
+import requests as req
+import nuvolaris.config as cfg
+
+class CouchDB:
+ def __init__(self):
+ self.db_protocol = "http"
+ self.db_prefix = "nuvolaris_"
+ self.db_port = cfg.get("couchdb.port", "COUCHDB_SERVICE_PORT", "5984")
+ self.db_host = cfg.get("couchdb.host", "COUCHDB_SERVICE_HOST", "couchdb")
+ self.db_username = cfg.get("couchdb.admin.user", "COUCHDB_ADMIN_USER", "whisk_admin")
+ self.db_password = cfg.get("couchdb.admin.password", "COUCHDB_ADMIN_PASSWORD", "some_passw0rd")
+ self.db_auth = req.auth.HTTPBasicAuth(self.db_username,self.db_password)
+ self.db_url = f"{self.db_protocol}://{self.db_host}:{self.db_port}"
+ self.db_base = f"{self.db_url}/{self.db_prefix}"
+ self.db_session = req.Session()
+ self.db_session.auth = self.db_auth
+
+ def wait_db_ready(self, max_seconds):
+ logging.info("entering CouchDB.wait_db_ready()")
+ start = time.time()
+ delta = 0
+ session = req.Session()
+ while delta < max_seconds:
+ try:
+ 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
+ except Exception as e:
+ logging.info(f"waiting since: {delta} seconds")
+ delta = int(time.time() - start)
+ time.sleep(1)
+ return False
+
+ # check if database exists, return boolean
+ def check_db(self, database):
+ url = f"{self.db_base}{database}"
+ r = self.db_session.head(url)
+ return r.status_code == 200
+
+ # delete database, return true if ok
+ def delete_db(self, database):
+ url = f"{self.db_base}{database}"
+ r = self.db_session.delete(url)
+ return r.status_code == 200
+
+ # create db, return true if ok
+ def create_db(self, database):
+ url = f"{self.db_base}{database}"
+ r = self.db_session.put(url)
+ return r.status_code == 201
+
+ # database="subjects"
+ def recreate_db(self, database, recreate=False):
+ msg = "recreate_db:"
+ exists = self.check_db(database)
+ if recreate and exists:
+ msg += " deleted"
+ self.delete_db(database)
+ if recreate or not exists:
+ msg += " created"
+ self.create_db(database)
+ return msg
+
+ def get_doc(self, database, id, user=None, password="", no_auth=False):
+ url = f"{self.db_base}{database}/{id}"
+ session = req.Session()
+ if no_auth:
+ db_auth=None
+ elif user:
+ db_auth=req.auth.HTTPBasicAuth(user, password)
+ else:
+ db_auth = self.db_auth
+ session.auth = db_auth
+ r = session.get(url)
+ if r.status_code == 200:
+ return json.loads(r.text)
+ return None
+
+ def update_doc(self, database, doc):
+ if '_id' in doc:
+ url = f"{self.db_base}{database}/{doc['_id']}"
+ cur = self.get_doc(database, doc['_id'])
+ if cur and '_rev' in cur:
+ doc['_rev'] = cur['_rev']
+ r = self.db_session.put(url, json=doc)
+ else:
+ r = self.db_session.put(url, json=doc)
+ return r.status_code in [200,201]
+ return False
+
+ def delete_doc(self, database, id):
+ cur = self.get_doc(database, id)
+ if cur and '_rev' in cur:
+ url = f"{self.db_base}{database}/{cur['_id']}?rev={cur['_rev']}"
+ r = self.db_session.delete(url)
+ return r.status_code == 200
+ return False
+
+ def configure_single_node(self):
+ url = f"{self.db_url}/_cluster_setup"
+ data = {"action": "enable_single_node", "singlenode": True, "bind_address": "0.0.0.0", "port": 5984}
+ r = self.db_session.post(url, json=data)
+ return r.status_code == 201
+
+ def configure_no_reduce_limit(self):
+ url = f"{self.db_url}/_node/_local/_config/query_server_config/reduce_limit"
+ data=b'"false"'
+ r = self.db_session.put(url, data=data)
+ return r.status_code == 200
+
+ def add_user(self, username: str, password: str):
+ userpass = {"name": username, "password": password, "roles": [], "type": "user"}
+ url = f"{self.db_url}/_users/org.couchdb.user:{username}"
+ res = self.db_session.put(url, json=userpass)
+ return res.status_code in [200, 201, 421]
+
+ #def add_role(self, database: str, members: list[str] = [], admins: list[str] =[]):
+ def add_role(self, database: str, members = [], admins =[]):
+ roles = {"admins": { "names": admins, "roles": [] }, "members": { "names": members, "roles": [] } }
+ url = f"{self.db_base}{database}/_security"
+ res = self.db_session.put(url, json=roles)
+ return res.status_code in [200, 201, 421]
+
+#
+# Submit a POST request to the _find endpoint using the specified selector
+#
+ def find_doc(self, database, selector, user=None, password="", no_auth=False):
+ url = f"{self.db_base}{database}/_find"
+ headers = {'Content-Type': 'application/json'}
+ session = req.Session()
+
+ if no_auth:
+ db_auth=None
+ elif user:
+ db_auth=req.auth.HTTPBasicAuth(user, password)
+ else:
+ db_auth = self.db_auth
+
+ session.auth=db_auth
+ r = session.post(url, headers=headers, data=selector)
+ if r.status_code == 200:
+ return json.loads(r.text)
+
+ logging.warn(f"query to {url} failed with {r.status_code}. Body {r.text}")
+ return None
\ No newline at end of file
diff --git a/actions/devel/redis/__main__.py b/actions/devel/redis/__main__.py
new file mode 100644
index 0000000..e183fdc
--- /dev/null
+++ b/actions/devel/redis/__main__.py
@@ -0,0 +1,72 @@
+# 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 json
+
+from common.authorize import Authorize
+from common.command_data import CommandData
+from command.redis import Redis
+from base64 import b64decode
+
+class ApiError(Exception):
+ pass
+
+def build_error(message: str):
+ return {
+ "statusCode": 400,
+ "body": message
+ }
+
+def build_response(data:CommandData):
+ meta_data = data.get_metadata()
+ return {
+ "statusCode": meta_data['status'],
+ "body": meta_data['result']
+ }
+
+def parse_body(args):
+ try:
+ return b64decode(args['__ow_body']).decode().strip()
+ except Exception as e:
+ print(e)
+ raise ApiError("could not parse __ow_body as base64")
+
+def main(args):
+ """
+ Action implementing a generic command wrapper for the nuv devel plugin. The action must be called with a POST request receiving a JSON
+ payload similar to this one
+ {
+ "command":"SET nuvolaris:key1 donald_duck"
+ }
+
+ the invoker must provide a x-impersonate-auth header containing the Openwhisk BASIC authentication of the wsku/user the action should impersonate
+ when submitting the command. Every specific provider will support specific request type and command.
+ WARNING: the body will be received as base64 encoded string as this action will be deployed with --web raw enabled flag
+ """
+ headers = args['__ow_headers']
+ if('x-impersonate-auth' not in headers):
+ return build_error("invalid request, missing mandatory header: x-impersonate-auth")
+
+ if(len(args['__ow_body']) == 0):
+ return build_error("invalid request, no command payload received")
+
+ try:
+ user_data = Authorize(args['couchdb_host'],args['couchdb_user'],args['couchdb_password']).login(headers['x-impersonate-auth'])
+ cmd = CommandData(json.loads(parse_body(args)))
+ return build_response(Redis(user_data).execute(cmd))
+ except Exception as e:
+ return build_error(f"failed to execute nuv devel redis. Reason: {str(e)}")
\ No newline at end of file
diff --git a/actions/devel/redis/command/redis.py b/actions/devel/redis/command/redis.py
new file mode 100644
index 0000000..9fa4a9e
--- /dev/null
+++ b/actions/devel/redis/command/redis.py
@@ -0,0 +1,55 @@
+# 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 common.util as ut
+import redis
+
+from common.command_data import CommandData
+
+class Redis():
+ """
+ Implementation of a Redis Command executor. It will require
+ a user_data dictionary linked to a specific user.
+ """
+
+ def __init__(self, user_data):
+ self._user_data = user_data
+ self._redis_prefix = ut.get_env_value(user_data,"REDIS_PREFIX")
+ self._redis_url = ut.get_env_value(user_data,"REDIS_URL")
+ self.validate()
+
+ def validate(self):
+ """
+ Validate that the provided user_data contains the appropriate
+ metadata for being able to submit a redis command.
+ """
+ if not self._redis_prefix or not self._redis_url:
+ raise Exception("user does not have valid REDIS environment set")
+
+ def execute(self, input:CommandData):
+ print(f"**** Redis command to execute {input.command()}")
+ try:
+ r = redis.from_url(self._redis_url)
+ result = r.execute_command(input.command())
+ input.result(str(result.decode('utf-8')))
+ input.status(200)
+ except Exception as e:
+ input.result(f"could not execute redis command {e}")
+ input.status(400)
+
+ return input
\ No newline at end of file
diff --git a/actions/devel/redis/common/authorize.py b/actions/devel/redis/common/authorize.py
new file mode 100644
index 0000000..f7d716f
--- /dev/null
+++ b/actions/devel/redis/common/authorize.py
@@ -0,0 +1,162 @@
+# 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.
+#
+from base64 import b64decode, b64encode
+from urllib.parse import quote, unquote
+
+import nuvolaris.config as cfg
+import nuvolaris.couchdb_util as cu
+import json
+import common.util as ut
+
+USER_META_DBN = "users_metadata"
+SUBJECT_META_DBN = "subjects"
+
+class DecodeError(Exception):
+ pass
+
+class EncodeError(Exception):
+ pass
+
+class AuthorizationError(Exception):
+ pass
+
+class Authorize():
+
+ def __init__(self, cdb_host, cdb_user, cdb_pwd):
+ cfg.clean()
+ cfg.put("couchdb.host", cdb_host)
+ cfg.put("couchdb.admin.user", cdb_user)
+ cfg.put("couchdb.admin.password", cdb_pwd)
+ self._db = cu.CouchDB()
+
+ def encode(self,username, password):
+ """Returns an HTTP basic authentication encrypted string given a valid
+ username and password.
+ """
+ if ':' in username:
+ raise EncodeError
+
+ username_password = f'{quote(username)}:{quote(password)}'
+ return f'Basic {b64encode(username_password.encode()).decode()}'
+
+ def _parse_b64(self, encoded_str):
+ try:
+ username, password = b64decode(encoded_str).decode().split(':', 1)
+ except:
+ # fallback in case the token is not bas64 encoded
+ username, password = encoded_str.split(':', 1)
+
+ if not username or not password:
+ raise DecodeError("authentication token does not seems to be b64 encoded")
+
+ return username, password
+
+ def decode(self, encoded_str):
+ """Decode an encrypted HTTP basic authentication string. Returns a tuple of
+ the form (username, password), and raises a DecodeError exception if
+ nothing could be decoded.
+ """
+ split = encoded_str.strip().split(' ')
+
+ # If split is only one element, try to decode the username and password
+ # directly.
+ if len(split) == 1:
+ username, password = self._parse_b64(split[0])
+
+ # If there are only two elements, check the first and ensure it says
+ # 'basic' so that we know we're about to decode the right thing. If not,
+ # bail out.
+ elif len(split) == 2:
+ if split[0].strip().lower() == 'basic':
+ username, password = self._parse_b64(split[1])
+ else:
+ raise DecodeError("authentication token provides more than 2 elements. could not parse properly")
+
+ # If there are more than 2 elements, something crazy must be happening.
+ # Bail.
+ else:
+ raise DecodeError("unpredictable error parsing authentication token")
+
+ return unquote(username), unquote(password)
+
+ def fetch_subject(self, uuid: str, key: str):
+ """
+ Query the internal couchdb searching for the subject mathing the given uuid, key.
+ Normally these stored in wsk or wsku in the form uuid:key
+ :param uuid the OW subject uuid
+ :param key the OW subject key
+ :return a ubject document
+ """
+ print(f"searching for openwhisk subject {uuid}")
+ try:
+ selector= {
+ "selector": {"namespaces": {"$elemMatch": {"uuid": uuid,"key": key}}}
+ }
+
+ response = self._db.find_doc(SUBJECT_META_DBN, json.dumps(selector))
+
+ if(response['docs']):
+ docs = list(response['docs'])
+ if(len(docs) > 0):
+ print(f"Nuvolaris namespace for user {uuid} found. Returning Result.")
+ return docs[0]
+
+ print(f"Nuvolaris metadata for user {uuid} not found!")
+ return None
+ except Exception as e:
+ print(f"failed to query Nuvolaris metadata for user {uuid}. Reason: {e}")
+ return None
+
+ def fetch_user_data(self, username: str):
+ """
+ Query the internal couchdb searching for the given principal to retrieve all the
+ relevant metadata
+ """
+ print(f"searching for user {username} meta-data")
+ try:
+ selector = {"selector":{"login": {"$eq": username }}}
+ response = self._db.find_doc(USER_META_DBN, json.dumps(selector))
+
+ if(response['docs']):
+ docs = list(response['docs'])
+ if(len(docs) > 0):
+ print(f"Nuvolaris metadata for user {username} found. Returning Result.")
+ return docs[0]
+
+ print(f"Nuvolaris metadata for user {username} not found!")
+ return None
+ except Exception as e:
+ print(f"failed to query Nuvolaris metadata for user {username}. Reason: {e}")
+ return None
+
+ def login(self, authorization: str):
+ """
+ Attempt to login the user identified by the given Openwhisk authorization AUTH token as base64
+ """
+ uuid, key = self.decode(authorization)
+ subject = self.fetch_subject(uuid, key)
+
+ if not subject:
+ raise AuthorizationError("Openwhisk subject not found.")
+
+ user_data = self.fetch_user_data(subject['subject'])
+
+ if user_data:
+ return user_data
+
+ raise AuthorizationError("Could not retrieve user metadata.")
\ No newline at end of file
diff --git a/actions/devel/redis/common/command_data.py b/actions/devel/redis/common/command_data.py
new file mode 100644
index 0000000..44f3faa
--- /dev/null
+++ b/actions/devel/redis/common/command_data.py
@@ -0,0 +1,50 @@
+# 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
... 62333 lines suppressed ...
(openserverless-operator) 17/24: chore: github actions fix
Posted by fr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
franztt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openserverless-operator.git
commit a5ae58386614fc262b0227cf6f46ea62ca4b0809
Author: francescotimperi <fr...@gmail.com>
AuthorDate: Wed Jul 24 17:36:09 2024 +0000
chore: github actions fix
---
.github/workflows/check.yml | 2 +-
.github/workflows/image.yml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml
index fb030d8..42657b6 100644
--- a/.github/workflows/check.yml
+++ b/.github/workflows/check.yml
@@ -28,7 +28,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-name: openserverless-operator
+name: openserverless-operator-check
on:
push:
tags-ignore:
diff --git a/.github/workflows/image.yml b/.github/workflows/image.yml
index 4b6ef87..5157287 100644
--- a/.github/workflows/image.yml
+++ b/.github/workflows/image.yml
@@ -29,7 +29,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-name: Build Image
+name: Build Openserverless-operator Image
on:
push:
(openserverless-operator) 12/24: Merge pull request #9 from francescotimperi/handler-fix
Posted by fr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
franztt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openserverless-operator.git
commit 5978eb416cef545283d976747d6b0958ee41f33b
Merge: 8c21a28 d2a919d
Author: Francesco Timperi Tiberi <fr...@gmail.com>
AuthorDate: Thu Jul 18 23:18:00 2024 +0200
Merge pull request #9 from francescotimperi/handler-fix
chore: cleanup unused demos
demos/hellonode/.gitignore | 19 -
demos/hellonode/Dockerfile | 26 -
demos/hellonode/app.js | 30 -
demos/hellonode/deploy.yaml | 55 -
demos/hellonode/package-lock.json | 1018 -----------------
demos/hellonode/package.json | 15 -
demos/operator/demo-crd.yaml | 71 --
demos/operator/demo-deployment.yaml | 37 -
demos/operator/demo-ns.yaml | 22 -
demos/operator/demo-obj.yaml | 26 -
demos/operator/demo1.py | 39 -
demos/operator/demo2.py | 61 -
demos/operator/demomock.py | 27 -
demos/operator/obj.yaml | 32 -
demos/operator/patch.yaml | 24 -
demos/operator/poetry.lock | 1179 --------------------
demos/operator/pyproject.toml | 35 -
demos/operator/transcript.txt | 78 --
demos/velero/Taskfile.yml | 154 ---
demos/velero/values.yaml | 538 ---------
deploy/_deprecated/preload-runtime-go117.yaml | 29 -
deploy/_deprecated/preload-runtime-nodej14.yaml | 28 -
deploy/_deprecated/preload-runtime-python37.yaml | 29 -
deploy/_deprecated/standalone-kcf.yaml | 77 --
deploy/_deprecated/standalone-lb.yaml | 29 -
deploy/_deprecated/standalone-np.yaml | 58 -
deploy/_deprecated/standalone-set.yaml | 61 -
deploy/_deprecated/standalone-test-in-https.yaml | 47 -
enterprise-util/couchdb/couchdb-nuvolaris-app.yaml | 42 -
.../couchdb/couchdb-nuvolaris-org-ingress.yaml | 42 -
enterprise-util/minio/minio-aks-ingress.yaml | 42 -
.../minio/minio-nuvolaris-org-ingress.yaml | 42 -
enterprise-util/minio/minio-saa1-ingress.yaml | 37 -
.../monitoring/prometheus-localhost.yaml | 37 -
.../prometheus-mistergpt-app-ingress.yaml | 37 -
.../prometheus-nuvolaris-app-ingress.yaml | 42 -
.../prometheus-nuvolaris-org-ingress.yaml | 42 -
enterprise/.gitkeep | 0
38 files changed, 4207 deletions(-)
(openserverless-operator) 13/24: ignoring .direnv
Posted by fr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
franztt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openserverless-operator.git
commit 11e176da7ed827d1c7a59e883f716c7302fdc06c
Author: Michele Sciabarra <mi...@sciabarra.com>
AuthorDate: Mon Jul 22 16:27:26 2024 +0200
ignoring .direnv
---
.gitignore | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/.gitignore b/.gitignore
index 5522c66..5d22165 100644
--- a/.gitignore
+++ b/.gitignore
@@ -53,4 +53,5 @@ whisk.properties
_failed.txt
deploy.tar
actions/github/callback/common
-actions/github/webhook/common
\ No newline at end of file
+actions/github/webhook/common
+.direnv/
(openserverless-operator) 03/24: chore: added basic setup for kaniko
Posted by fr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
franztt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openserverless-operator.git
commit 222cbda41ecd54e84fe55446edcfc748ec106be2
Author: francescotimperi <fr...@gmail.com>
AuthorDate: Fri Jul 12 21:32:01 2024 +0000
chore: added basic setup for kaniko
---
.envrc | 20 ++++++++++++++++++++
Taskfile.yml | 16 +++++++++++++++-
kaniko.yaml | 26 ++++++++++++++++++++++++++
shell.nix | 33 +++++++++++++++++++++++++++++++++
4 files changed, 94 insertions(+), 1 deletion(-)
diff --git a/.envrc b/.envrc
new file mode 100644
index 0000000..bc6295d
--- /dev/null
+++ b/.envrc
@@ -0,0 +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.
+#
+
+use nix
+
diff --git a/Taskfile.yml b/Taskfile.yml
index a88e0cd..f138fbf 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -22,11 +22,13 @@ vars:
N: ""
T: ""
P: ""
- BASETAG: 1.3.0-mastrogpt
+ BASETAG: 0.1-dev
KUBE:
sh: ./detect.sh
NS: "nuvolaris"
PREFIX: ""
+ TAG:
+ sh: git describe --tags --abbrev=0 2>/dev/null || echo latest
dotenv:
- .env
@@ -326,6 +328,18 @@ tasks:
docker-login:
- task: b:docker-login
+ install-registry:
+ - helm repo add twuni https://helm.twun.io
+ - helm install docker-registry twuni/docker-registry --namespace default
+
+ build:
+ env:
+ TAG: "{{.TAG}}"
+ cmds:
+ - test -n "$GITHUB_USER" || true "did you configure your .env?"
+ - envsubst <kaniko.yaml >_kaniko.yaml
+ - cat _kaniko.yaml
+
shell:
- kubectl -n nuvolaris exec --stdin --tty {{.POD}} -- /bin/bash
diff --git a/kaniko.yaml b/kaniko.yaml
new file mode 100644
index 0000000..eeac3f4
--- /dev/null
+++ b/kaniko.yaml
@@ -0,0 +1,26 @@
+apiVersion: v1
+kind: Pod
+metadata:
+ name: kaniko
+ namespace: default
+spec:
+ containers:
+ - name: kaniko
+ #image: busybox
+ #command:
+ #- sleep
+ #- inf
+ image: gcr.io/kaniko-project/executor:latest
+ args:
+ - --context=git://github.com/${GITHUB_USER}/openserverless-operator
+ - --destination=docker-registry:5000/openserverless/operator:${TAG}
+ - --insecure
+ - --insecure-registry=docker-registry
+ volumeMounts:
+ - name: config-volume
+ mountPath: /context
+ restartPolicy: Never
+ volumes:
+ - name: config-volume
+ configMap:
+ name: dockerfile-configmap
diff --git a/shell.nix b/shell.nix
new file mode 100644
index 0000000..537facb
--- /dev/null
+++ b/shell.nix
@@ -0,0 +1,33 @@
+# 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.
+#
+
+let
+ nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-23.11";
+ pkgs = import nixpkgs { config = {}; overlays = []; };
+in
+
+pkgs.mkShellNoCC {
+ packages = with pkgs; [
+ python3
+ kubernetes-helm
+ poetry
+ zip
+ unzip
+ ];
+
+}
(openserverless-operator) 21/24: Merge pull request #13 from francescotimperi/main
Posted by fr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
franztt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openserverless-operator.git
commit f029daecaca2d8a558e6e31caa96071c187df441
Merge: ad9b05d 6d20f91
Author: Francesco Timperi Tiberi <fr...@gmail.com>
AuthorDate: Wed Jul 24 21:00:06 2024 +0200
Merge pull request #13 from francescotimperi/main
Adds operator GitHub actions to build operator images
.github/workflows/check.yml | 2 +-
.github/workflows/fast-image.yml | 84 ----------------------------------------
.github/workflows/image.yml | 66 ++++++++++---------------------
.licenserc.yaml | 3 ++
Dockerfile | 4 +-
5 files changed, 27 insertions(+), 132 deletions(-)
(openserverless-operator) 16/24: chore: preparing github actions to build operator
Posted by fr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
franztt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openserverless-operator.git
commit 18ce9f5f19f827ddfaff8e186fef2bf3337f99b1
Author: francescotimperi <fr...@gmail.com>
AuthorDate: Wed Jul 24 17:34:17 2024 +0000
chore: preparing github actions to build operator
---
.github/workflows/fast-image.yml | 84 ----------------------------------------
.github/workflows/image.yml | 46 +++-------------------
Dockerfile | 4 +-
3 files changed, 8 insertions(+), 126 deletions(-)
diff --git a/.github/workflows/fast-image.yml b/.github/workflows/fast-image.yml
deleted file mode 100644
index 65eb5ce..0000000
--- a/.github/workflows/fast-image.yml
+++ /dev/null
@@ -1,84 +0,0 @@
-# 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.
-#
-# Copyright 2021 Nuvolaris LLC
-#
-# Licensed 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.
-
-name: Build Fast Image
-
-on:
- push:
- tags:
- - 'fast[0-9]*'
- branches-ignore:
- - '*'
-
-jobs:
- image:
- name: Image
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v2
- with:
- submodules: recursive
- - name: Make envfile
- if: ${{ github.repository_owner != 'nuvolaris'}}
- uses: SpicyPizza/create-envfile@v1.3
- with:
- envkey_MY_OPERATOR_IMAGE: ${{ secrets.MY_OPERATOR_IMAGE }}
- envkey_GITHUB_TOKEN: ${{ secrets.GT_TOKEN }}
- envkey_GITHUB_USER: ${{ secrets.GT_USER }}
- file_name: .env
- fail_on_empty: false
- - name: Registry
- uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
- with:
- registry: ghcr.io
- username: ${{ github.actor }}
- password: ${{ secrets.GITHUB_TOKEN }}
- - name: Setup
- run: bash .github/cisetup.sh
- - name: Ngrok
- if: ${{ env.NGROK == 'on' }}
- run: bash .github/debug-github-actions.sh
- env:
- NGROK_TOKEN: ${{ secrets.NGROK_TOKEN }}
- USER_PASS: ${{ secrets.USER_PASS }}
- - name: Wait
- if: ${{ env.NGROK == 'on' }}
- run: bash .github/wait.sh
- - name: DockerLogin
- if: ${{ github.repository_owner != 'nuvolaris'}}
- run: task docker-login
- - name: Buildx
- uses: crazy-max/ghaction-docker-buildx@v1
- with:
- version: latest
- - name: Image
- run: task buildx-and-push
\ No newline at end of file
diff --git a/.github/workflows/image.yml b/.github/workflows/image.yml
index 2e44bd4..4b6ef87 100644
--- a/.github/workflows/image.yml
+++ b/.github/workflows/image.yml
@@ -52,51 +52,17 @@ jobs:
with:
submodules: recursive
- name: License
- uses: apache/skywalking-eyes@main
- - name: Make envfile
- if: ${{ github.repository_owner != 'nuvolaris'}}
- uses: SpicyPizza/create-envfile@v1.3
+ uses: apache/skywalking-eyes@main
+ - name: Registry login
+ uses: docker/login-action@v3
with:
- envkey_MY_OPERATOR_IMAGE: ${{ secrets.MY_OPERATOR_IMAGE }}
- envkey_GITHUB_TOKEN: ${{ secrets.GT_TOKEN }}
- envkey_GITHUB_USER: ${{ secrets.GT_USER }}
- file_name: .env
- fail_on_empty: false
- - name: Registry
- uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
- with:
- registry: ghcr.io
- username: ${{ github.actor }}
- password: ${{ secrets.GITHUB_TOKEN }}
+ registry: registry.hub.docker.com
+ username: ${{ secrets.DOCKERHUB_USER }}
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Setup
run: bash .github/cisetup.sh
- - name: Kind
- uses: helm/kind-action@v1.2.0
- with:
- cluster_name: nuvolaris
- config: .github/kind.yaml
- name: Install
run: poetry install
- #- name: Tag
- # run: task image-tag
- #- name: BuildAndLoad
- # run: task build-and-load
- #- name: Ngrok
- # if: ${{ env.NGROK == 'on' }}
- # run: bash .github/debug-github-actions.sh
- # env:
- # NGROK_TOKEN: ${{ secrets.NGROK_TOKEN }}
- # USER_PASS: ${{ secrets.USER_PASS }}
- #- name: Wait
- # if: ${{ env.NGROK == 'on' }}
- # run: bash .github/wait.sh
- #- name: AddIngress
- # run: task kind:ingress
- #- name: Test
- # run: task test
- - name: DockerLogin
- if: ${{ github.repository_owner != 'nuvolaris'}}
- run: task docker-login
- name: Buildx
uses: crazy-max/ghaction-docker-buildx@v1
with:
diff --git a/Dockerfile b/Dockerfile
index 887d551..92ecf70 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -20,8 +20,8 @@ ENV CONTROLLER_IMAGE=ghcr.io/nuvolaris/openwhisk-controller
ENV CONTROLLER_TAG=3.1.0-mastrogpt.2402101445
ENV INVOKER_IMAGE=ghcr.io/nuvolaris/openwhisk-invoker
ENV INVOKER_TAG=3.1.0-mastrogpt.2402101445
-ARG OPERATOR_IMAGE_DEFAULT=ghcr.io/nuvolaris/openserverless-operator
-ARG OPERATOR_TAG_DEFAULT=0.1-dev.2309191654
+ARG OPERATOR_IMAGE_DEFAULT=registry.hub.docker.com/apacheopenserverless/openserverless-operator
+ARG OPERATOR_TAG_DEFAULT=0.1-snapshot.2309191654
ENV OPERATOR_IMAGE=${OPERATOR_IMAGE_DEFAULT}
ENV OPERATOR_TAG=${OPERATOR_TAG_DEFAULT}
(openserverless-operator) 24/24: Merge pull request #14 from francescotimperi/main
Posted by fr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
franztt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openserverless-operator.git
commit af0d82b0192f577fd6321e02f7a1f49f26d71e45
Merge: f029dae 1757d71
Author: Francesco Timperi Tiberi <fr...@gmail.com>
AuthorDate: Sat Jul 27 18:22:11 2024 +0200
Merge pull request #14 from francescotimperi/main
fix cron scheduler
.github/workflows/image.yml | 6 +++++-
Dockerfile | 4 ++--
Taskfile.yml | 2 +-
nuvolaris/actionexecutor.py | 8 ++++----
4 files changed, 12 insertions(+), 8 deletions(-)
(openserverless-operator) 20/24: chore: setup multiplatform images
Posted by fr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
franztt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openserverless-operator.git
commit 6d20f9141b86c5718cdbb7e888a35cb1d395d139
Author: francescotimperi <fr...@gmail.com>
AuthorDate: Wed Jul 24 18:33:41 2024 +0000
chore: setup multiplatform images
---
.github/workflows/image.yml | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/image.yml b/.github/workflows/image.yml
index 2af8c8a..60c2732 100644
--- a/.github/workflows/image.yml
+++ b/.github/workflows/image.yml
@@ -73,9 +73,9 @@ jobs:
run: bash .github/cisetup.sh
- name: Install
run: poetry install
- - name: Buildx
- uses: crazy-max/ghaction-docker-buildx@v1
- with:
- version: latest
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v3
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
- name: Image
run: task buildx-and-push TAG="$IMG_TAG"
\ No newline at end of file