diff --git a/application-templates/django-app/api/test_st.py b/application-templates/django-app/api/test_st.py
new file mode 100644
index 00000000..fb852433
--- /dev/null
+++ b/application-templates/django-app/api/test_st.py
@@ -0,0 +1,27 @@
+import os
+from pprint import pprint
+import schemathesis as st
+from schemathesis.checks import response_schema_conformance, not_a_server_error
+
+from cloudharness_test import apitest_init # include to perform default authorization
+
+app_url = os.environ.get("APP_URL", "http://samples.ch.local/api")
+
+try:
+ schema = st.from_uri(app_url + "/openapi.json")
+except:
+ # support alternative schema location
+ schema = st.from_uri(app_url.replace("/api", "") + "/openapi.json")
+
+
+@schema.parametrize(endpoint="/ping")
+def test_ping(case):
+ response = case.call()
+ pprint(response.__dict__)
+ assert response.status_code == 200, "this api errors on purpose"
+
+def test_state_machine():
+ schema.as_state_machine().run()
+# APIWorkflow = schema.as_state_machine()
+# APIWorkflow.run()
+# TestAPI = APIWorkflow.TestCase
\ No newline at end of file
diff --git a/cloudharness.png b/cloudharness.png
index 1966e6dd..9e151f12 100644
Binary files a/cloudharness.png and b/cloudharness.png differ
diff --git a/deployment-configuration/helm/templates/auto-database.yaml b/deployment-configuration/helm/templates/auto-database.yaml
index 2deba7fe..6ec31380 100644
--- a/deployment-configuration/helm/templates/auto-database.yaml
+++ b/deployment-configuration/helm/templates/auto-database.yaml
@@ -74,11 +74,6 @@ spec:
volumeMounts:
- name: {{ .app.harness.database.name | quote }}
mountPath: /data/db
- {{- if .root.Values.backup.active }}
- - name: "db-backups"
- mountPath: {{ (printf "%s/%s/%s" .root.Values.backup.dir .app.harness.database.type .app.harness.database.name) | quote }}
- readOnly: true
- {{- end }}
{{- if eq .app.harness.database.type "postgres" }}
- mountPath: /dev/shm
name: dshm
@@ -92,11 +87,6 @@ spec:
medium: Memory
name: dshm
{{- end }}
- {{- if .root.Values.backup.active }}
- - name: "db-backups"
- persistentVolumeClaim:
- claimName: "db-backups"
- {{- end }}
---
{{- if .root.Values.backup.active }}
{{- include (print "deploy_utils.database." .app.harness.database.type ".backup") . }}
diff --git a/deployment-configuration/helm/templates/auto-gatekeepers.yaml b/deployment-configuration/helm/templates/auto-gatekeepers.yaml
index 47815797..898995cd 100644
--- a/deployment-configuration/helm/templates/auto-gatekeepers.yaml
+++ b/deployment-configuration/helm/templates/auto-gatekeepers.yaml
@@ -18,8 +18,12 @@ data:
enable-default-deny: {{ eq (.app.harness.secured | toString) "true" }}
listen: 0.0.0.0:8080
enable-refresh-tokens: true
- server-write-timeout: 180s
- upstream-response-header-timeout: 180s
+ server-write-timeout: {{ .app.harness.proxy.timeout.send | default .root.Values.proxy.timeout.send | default 180 }}s
+ upstream-timeout: {{ .app.harness.proxy.timeout.read | default .root.Values.proxy.timeout.read | default 180 }}s
+ upstream-response-header-timeout: {{ .app.harness.proxy.timeout.read | default .root.Values.proxy.timeout.read | default 180 }}s
+ upstream-expect-continue-timeout: {{ .app.harness.proxy.timeout.read | default .root.Values.proxy.timeout.read | default 180 }}s
+ server-read-timeout: {{ .app.harness.proxy.timeout.read | default .root.Values.proxy.timeout.read | default 180 }}s
+ upstream-keepalive-timeout: {{ .app.harness.proxy.timeout.keepalive | default .root.Values.proxy.timeout.keepalive | default 180 }}s
http-only-cookie: false
tls-cert:
tls-private-key:
@@ -65,7 +69,7 @@ data:
403 Permission Denied
Sorry, you do not have access to this page, please contact your administrator.
- If you have been assigned new authorizations try to
login again.
+ If you have been assigned new authorizations, try to refresh the page or to
login again.
diff --git a/deployment-configuration/helm/templates/ingress.yaml b/deployment-configuration/helm/templates/ingress.yaml
index af59d0a4..38b568a2 100644
--- a/deployment-configuration/helm/templates/ingress.yaml
+++ b/deployment-configuration/helm/templates/ingress.yaml
@@ -38,10 +38,13 @@ metadata:
cert-manager.io/issuer: {{ printf "%s-%s" "letsencrypt" .Values.namespace }}
{{- end }}
nginx.ingress.kubernetes.io/ssl-redirect: {{ (and $tls .Values.ingress.ssl_redirect) | quote }}
- nginx.ingress.kubernetes.io/proxy-body-size: '250m'
+ nginx.ingress.kubernetes.io/proxy-body-size: '{{ .Values.proxy.payload.max }}m'
nginx.ingress.kubernetes.io/proxy-buffer-size: '128k'
nginx.ingress.kubernetes.io/from-to-www-redirect: 'true'
nginx.ingress.kubernetes.io/rewrite-target: /$1
+ nginx.ingress.kubernetes.io/auth-keepalive-timeout: {{ .Values.proxy.timeout.keepalive | quote }}
+ nginx.ingress.kubernetes.io/proxy-read-timeout: {{ .Values.proxy.timeout.read | quote }}
+ nginx.ingress.kubernetes.io/proxy-send-timeout: {{ .Values.proxy.timeout.send | quote }}
spec:
rules:
{{- range $app := .Values.apps }}
diff --git a/deployment-configuration/helm/values.yaml b/deployment-configuration/helm/values.yaml
index 6cf16d1e..434dcac7 100644
--- a/deployment-configuration/helm/values.yaml
+++ b/deployment-configuration/helm/values.yaml
@@ -67,3 +67,13 @@ backup:
memory: "64Mi"
# -- K8s cpu resource definition.
cpu: "50m"
+proxy:
+ timeout:
+ # -- Timeout for proxy connections in seconds.
+ send: 60
+ # -- Timeout for proxy responses in seconds.
+ read: 60
+ keepalive: 60
+ payload:
+ # -- Maximum size of payload in MB
+ max: 250
diff --git a/deployment-configuration/value-template.yaml b/deployment-configuration/value-template.yaml
index 16c00b3a..8702e6f6 100644
--- a/deployment-configuration/value-template.yaml
+++ b/deployment-configuration/value-template.yaml
@@ -22,6 +22,8 @@ harness:
- administrator
- uri: /api/openapi.json
white-listed: true
+ - uri: /openapi.json
+ white-listed: true
# -- Defines reference deployment parameters. Values maps to k8s spec
deployment:
# -- When true, enables automatic deployment
@@ -125,3 +127,13 @@ harness:
smoketest: true
ignoreConsoleErrors: false
ignoreRequestErrors: false
+ proxy:
+ timeout:
+ # -- Timeout for proxy connections in seconds.
+ send:
+ # -- Timeout for proxy responses in seconds.
+ read:
+ keepalive:
+ payload:
+ # -- Maximum size of payload in MB
+ max:
\ No newline at end of file
diff --git a/docs/accounts.md b/docs/accounts.md
index 8b52ed2f..8d07ca4a 100644
--- a/docs/accounts.md
+++ b/docs/accounts.md
@@ -62,7 +62,22 @@ harness:
secured: open
```
+#### Proxy specific configurations
+Proxy configurations can be personalized in the application in the case that we want to have more restrictive values than the global ones (see [here](./ingress-domains-proxies.md#proxy-configurations) for more )
+```yaml
+harness:
+ proxy:
+ timeout:
+ # -- Timeout for proxy connections in seconds.
+ send:
+ # -- Timeout for proxy responses in seconds.
+ read:
+ keepalive:
+ payload:
+ # -- Maximum size of payload in MB
+ max:
+```
### Secure an enpoint with OpenAPI
In every api endpoint that you want to secure, add the bearerAuth security as in the example:
diff --git a/docs/applications/databases.md b/docs/applications/databases.md
index 6aa6af13..89bd2868 100644
--- a/docs/applications/databases.md
+++ b/docs/applications/databases.md
@@ -111,14 +111,49 @@ Per default, database backups are disabled. However, you can overwrite backups b
```yaml
backup:
- active: true
+ active: true
```
+See all the default values [here](../../deployment-configuration/helm/values.yaml).
You can find additional configuration fields for backups to overwrite in the generated `deployment/helm/values.yaml` once you deploy your applications.
Backups are defined for `mongo` and `postgres` database in form of a [K8s CronJob](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/) that creates a dump of the database and stores it in a different persistent volume on the same cluster.
-This is done periodically according to a configurable schedule, per default once a day.
+This is done periodically according to a configurable schedule, per default every 5 minutes.
+
+A smart retention strategy is used for backups, by default:
+- all current days backups
+- one per day, last 7 days
+- one per week, last 4 weeks
+- one per month, last 6 months
+
+Implementation of backups and retention is based on https://github.com/prodrigestivill/docker-postgres-backup-local.
+
+#### How to monitor and restore backups
+
+Backups are stored in a Kubernetes volume named `db-backups`.
+
+Can mount the volume to your database pod by adding the following to your db deployment:
+
+```yaml
+...
+spec:
+ template:
+ spec:
+ containers:
+ - ...
+ volumeMounts:
+ - name: "db-backups"
+ mountPath: /backups
+ readOnly: true
+ ...
+ volumes:
+ ...
+ - name: "db-backups"
+ persistentVolumeClaim:
+ claimName: "db-backups"
+```
+
### MongoDB
diff --git a/docs/ingress-domains-proxies.md b/docs/ingress-domains-proxies.md
new file mode 100644
index 00000000..49903818
--- /dev/null
+++ b/docs/ingress-domains-proxies.md
@@ -0,0 +1,69 @@
+# Ingress, domains and proxies
+
+## Default configurations for domain and subdomains
+Cloud Harness makes it very easy to configure domains and proxies, by making
+an underlying assumption:
+
+- Applications share a main base domain (say ch.org)
+- Applications can define a subdomain (say myapp)
+
+The main domain is configured in the [root values file](../deployment-configuration/values-template.yaml) and
+it is usually overridden by the `harness-deployment` command, e.g.
+
+```
+harness-deployment ... -d ch.org
+```
+
+The subdomain is defined in the application's values.yaml file in
+harness.subdomain (see for instance the [samples application configuration](../applications/samples/deploy/values.yaml))
+
+For instance on applications/myapp/deploy/values.yaml:
+
+```yaml
+harness:
+ subdomain: myapp
+```
+
+The above configurations put together create an ingress configuration for https://myapp.ch.org and automatically configure letsencrypt to create and renew certificates.
+
+Note:
+that the tls and letsencrypt configurations are enabled by default but should usually be disabled locally with
+
+```
+harness-deployment ... -dtls -l
+```
+
+## Main application
+
+The "main" application is deployed on the base domain.
+In order to specify a main application, override the value in your `/deployment-configuration/values-template.yaml` file.
+
+Example
+```yaml
+mainapp: myapp
+```
+This creates a reverse proxy to https://ch.org pointing to myapp
+
+## Proxy configurations
+
+Ingress is a reverse proxy and as such has some configurations to take into account.
+The most common configurations are connection timeouts and payload size.
+
+To configure it, override the following values in your `deployment-configuration/values-template.yaml` file.
+
+```yaml
+proxy:
+ timeout:
+ # -- Timeout for proxy connections in seconds.
+ send: 60
+ # -- Timeout for proxy responses in seconds.
+ read: 60
+ keepalive: 60
+ payload:
+ # -- Maximum size of payload in MB
+ max: 250
+```
+
+Note that in the case that gatekeepers are enabled, the same configurations are applied
+to the gatekeepers, unless the application override them on `harness.proxy.*`.
+See also the [gatekeepers documentation](./accounts.md#secure-and-enpoint-with-the-gatekeeper).
\ No newline at end of file
diff --git a/docs/model/ApplicationHarnessConfig.md b/docs/model/ApplicationHarnessConfig.md
index a83f853a..8e8a251d 100644
--- a/docs/model/ApplicationHarnessConfig.md
+++ b/docs/model/ApplicationHarnessConfig.md
@@ -30,6 +30,7 @@ Key | Input Type | Accessed Type | Description | Notes
**jupyterhub** | [**JupyterHubConfig**](JupyterHubConfig.md) | [**JupyterHubConfig**](JupyterHubConfig.md) | | [optional]
**accounts** | [**ApplicationAccountsConfig**](ApplicationAccountsConfig.md) | [**ApplicationAccountsConfig**](ApplicationAccountsConfig.md) | | [optional]
**test** | [**ApplicationTestConfig**](ApplicationTestConfig.md) | [**ApplicationTestConfig**](ApplicationTestConfig.md) | | [optional]
+**quotas** | [**Quota**](Quota.md) | [**Quota**](Quota.md) | | [optional]
**any_string_name** | dict, frozendict.frozendict, str, date, datetime, uuid.UUID, int, float, decimal.Decimal, bool, None, list, tuple, bytes, io.FileIO, io.BufferedReader, | frozendict.frozendict, str, decimal.Decimal, BoolClass, NoneClass, tuple, bytes, FileIO | any string name can be used but the value must be the correct type | [optional]
# aliases
diff --git a/docs/model/HarnessMainConfig.md b/docs/model/HarnessMainConfig.md
index d7d72df1..9f00bc2b 100644
--- a/docs/model/HarnessMainConfig.md
+++ b/docs/model/HarnessMainConfig.md
@@ -21,7 +21,7 @@ Key | Input Type | Accessed Type | Description | Notes
**backup** | [**BackupConfig**](BackupConfig.md) | [**BackupConfig**](BackupConfig.md) | | [optional]
**name** | str, | str, | Base name | [optional]
**task-images** | [**SimpleMap**](SimpleMap.md) | [**SimpleMap**](SimpleMap.md) | | [optional]
-**any_string_name** | dict, frozendict.frozendict, str, date, datetime, int, float, bool, decimal.Decimal, None, list, tuple, bytes, io.FileIO, io.BufferedReader | frozendict.frozendict, str, BoolClass, decimal.Decimal, NoneClass, tuple, bytes, FileIO | any string name can be used but the value must be the correct type | [optional]
+**any_string_name** | dict, frozendict.frozendict, str, date, datetime, uuid.UUID, int, float, decimal.Decimal, bool, None, list, tuple, bytes, io.FileIO, io.BufferedReader, | frozendict.frozendict, str, decimal.Decimal, BoolClass, NoneClass, tuple, bytes, FileIO | any string name can be used but the value must be the correct type | [optional]
# env
diff --git a/docs/model/Quota.md b/docs/model/Quota.md
index ddf78dea..8124fe92 100644
--- a/docs/model/Quota.md
+++ b/docs/model/Quota.md
@@ -8,7 +8,7 @@ dict, frozendict.frozendict, | frozendict.frozendict, | |
### Dictionary Keys
Key | Input Type | Accessed Type | Description | Notes
------------ | ------------- | ------------- | ------------- | -------------
-**any_string_name** | str, | str, | any string name can be used but the value must be the correct type | [optional]
+**any_string_name** | dict, frozendict.frozendict, str, date, datetime, uuid.UUID, int, float, decimal.Decimal, bool, None, list, tuple, bytes, io.FileIO, io.BufferedReader, | frozendict.frozendict, str, decimal.Decimal, BoolClass, NoneClass, tuple, bytes, FileIO | any string name can be used but the value must be the correct type | [optional]
[[Back to Model list]](../../README.md#documentation-for-models) [[Back to API list]](../../README.md#documentation-for-api-endpoints) [[Back to README]](../../README.md)
diff --git a/docs/model/SimpleMap.md b/docs/model/SimpleMap.md
index 4106066b..37074b56 100644
--- a/docs/model/SimpleMap.md
+++ b/docs/model/SimpleMap.md
@@ -8,7 +8,7 @@ dict, frozendict.frozendict, | frozendict.frozendict, | |
### Dictionary Keys
Key | Input Type | Accessed Type | Description | Notes
------------ | ------------- | ------------- | ------------- | -------------
-**any_string_name** | str, | str, | any string name can be used but the value must be the correct type | [optional]
+**any_string_name** | dict, frozendict.frozendict, str, date, datetime, uuid.UUID, int, float, decimal.Decimal, bool, None, list, tuple, bytes, io.FileIO, io.BufferedReader, | frozendict.frozendict, str, decimal.Decimal, BoolClass, NoneClass, tuple, bytes, FileIO | any string name can be used but the value must be the correct type | [optional]
[[Back to Model list]](../../README.md#documentation-for-models) [[Back to API list]](../../README.md#documentation-for-api-endpoints) [[Back to README]](../../README.md)
diff --git a/docs/testing.md b/docs/testing.md
index 11fd8349..27356fea 100644
--- a/docs/testing.md
+++ b/docs/testing.md
@@ -108,16 +108,7 @@ The test can use environmental variables:
Examples:
- [Sample api test](../applications/samples/test/api/test_st.py)
-### Common smoke tests
-
-Once a test is created for your application, generic smoke tests are also
-executed, checking for:
-- Main page is reachable
-- No errors in the console
-- No error responses from network resources and fetch requests (code < 400)
-
-The smoke tests is defined [in this file](../test/test-e2e/__tests__/common.spec.ts).
@@ -125,6 +116,22 @@ The smoke tests is defined [in this file](../test/test-e2e/__tests__/common.spec
End to end tests run in a headless browser ([Puppeteer](https://github.com/puppeteer/puppeteer)) against the full deployment on Kubernetes.
+Custom configuration:
+
+```yaml
+harness:
+ ...
+ test:
+ e2e:
+ # -- enable/disable e2e tests
+ enabled: true
+ # -- ignore errors on console by default
+ ignoreConsoleErrors: false
+ # -- ignore fetched resources errors by default
+ ignoreRequestErrors: false
+ # -- enable common smoke tests
+ smoketest: true
+```
### Write tests with Jest and Puppeteer
@@ -159,7 +166,7 @@ executed, checking for:
- No errors in the console
- No error responses from network resources and fetch requests (code < 400)
-The smoke tests is defined [in this file](../test/jest-puppeteer/__tests__/common.spec.ts).
+The smoke tests are defined [in this file](../test/jest-puppeteer/__tests__/common.spec.ts).
## Run API and E2E tests in the CI/CD pipeline
@@ -182,7 +189,7 @@ deployment.
In order to use `harness-test` install the library with
```
-pip install -r requirements-test.txt
+pip install -e tools/cloudharness-test
```
In order to run tests against an existing deployment based on a domain (say, my.domain), run:
@@ -198,6 +205,25 @@ If you want to run the deployment locally and then run the tests, can use skaffo
1. Wait the deployment to settle
1. Run `harness-test PATHS`
+### Tests development
+The `harness-test` client is useful while developing and tweaking the tests.
+In that case it's better to target the application under development and
+the kind of tests we are working on.
+
+
+To target a specific application for end-to-end tests, use:
+```
+harness-test . -i [APPNAME] -e
+```
+
+To target a specific application for api tests, use:
+```
+harness-test . -i [APPNAME] -a
+```
+
+Note that the local version of the openapi.yaml file located at applications/[APPNAME]/api/openapi.yaml is used if available. That's useful to tweak examples and responses
+used by schemathesis to generate the test hypotheses.
+
## Create test users for your application
To create test users:
diff --git a/docs/tutorials/cloud-harness-wsl2-setup.md b/docs/tutorials/cloud-harness-wsl2-setup.md
new file mode 100644
index 00000000..41f839d1
--- /dev/null
+++ b/docs/tutorials/cloud-harness-wsl2-setup.md
@@ -0,0 +1,70 @@
+
+### The first thing to check
+
+> Make sure you have JDK installed in your WSL system. Without this, the code is not generated! (with harness-application command). A bunch of errors that I had was solved by this step. In addition, test if you have all the basic requirements needed for the Cloud-Harness + other requirements needed to set up.
+
+Please check if you are running **Kubernetes with Docker Desktop.**
+
+If you have Codecloud VS extension, make sure to check that you have docker-desktop ACTIVE. You can also check the namespace which by default is set to `default`
+
+### Setting namespaces and with `docker-desktop` context
+
+> The commands related to minikube from the tutorial are not needed in the case of docker-desktop.
+
+We create namespaces inside docker-desktop… for our case of following the tutorial, it is azathoth
+
+```
+kubectl create ns azathoth
+```
+
+If want to change the namespace while keeping the context same (i.e. docker-desktop) then the following is the command. This is because the default namespace is `default`
+
+```
+kubectl config set-context --current --namespace=azathoth
+```
+
+### Deployment command distinction
+
+Use the following command when you want your application installed inside the cloud-harness directory (very less likely; that one would use this)
+
+```
+harness-deployment . -u -dtls -l -d azathoth.local -e local -n azathoth
+```
+
+Otherwise (mostly how we use it)
+
+```
+harness-deployment cloud-harness . -u -dtls -l -d azathoth.local -e local -n azathoth
+```
+
+### About host file issue
+
+To be able to visit links like - [http://clockdate.azathoth.local/](http://clockdate.azathoth.local/), or [http://clockdate.azathoth.local/api/ping](http://clockdate.azathoth.local/api/ping), we need to update the host file. Make note that the host file is of Windows (and not for the subsystem), which can be found here:
+
+```
+C:\Windows\System32\drivers\etc\hosts
+```
+
+(NOTE: We might be able to work with Linux hosts if we enable - *_Add the .docker.internal names to the host's etc/hosts file (Requires password)_ in the General settings of docker desktop. But I haven’t tested that. )
+
+### If Running CLOUD HARNESS for the first time!
+
+There are certain scripts that need to be run when running the CloudHarness and its dependent applications for the first time in a system. These are `sc.yaml` and `cluster-init.sh` which can be found in the following path w.r.t. cloud-harness.
+
+```
+kubectl apply -f cloudharness/deployment/sc.yaml
+```
+
+```
+cd cloud-harness/infrastructure/cluster-configuration && bash cluster-init.sh
+```
+
+These are one-time setup scripts needed to be run.
+
+### About Frontend issues
+
+If the [clockdate.azathoth.local](http://clockdate.azathoth.local/) doesn’t return the frontend part!
+
+When the frontend is generated through the “harness-application”, the package-lock.json is required which is not mentioned in the docs. Therefore one must have to do the `npm i —legacy-peer-deps` before proceeding with the “scaffold run” command.
+
+
diff --git a/docs/tutorials/simple-date-clock-application.adoc b/docs/tutorials/simple-date-clock-application.adoc
index 66208042..8ed52a0e 100644
--- a/docs/tutorials/simple-date-clock-application.adoc
+++ b/docs/tutorials/simple-date-clock-application.adoc
@@ -71,9 +71,15 @@ If everything is good, you're in the right path to use {ch}!
== How to setup your local cluster using minikube (if not done yet)
+
{ch} is designed to help you generate all the required artifacts for a deployment on a Kubernetes cluster.
For a local deployment, you can use minikube, which is basically https://minikube.sigs.k8s.io/docs/start/[a small {kub} cluster on your machine].
+
+[IMPORTANT - WSL2]
+If you're following this tutorial in a Windows WSL2 - then check => link:./cloud-harness-wsl2-setup.md[cloud-harness-wsl2-setup] for help!
+
+
[IMPORTANT]
The setup of a {kub} cluster is not mandatory to use {ch}.
This step is proposed in this tutorial to show you how to deploy an application generated with {ch} on a local cluster and to appreciate how much {ch} reduces the pain of writing deployment configuration artifacts.
@@ -147,7 +153,7 @@ If you don't create the namespace, the deployment will fail!
[source,bash]
----
# ran from the cloud-harness repository root
-harness-deployment . -u -dtls -l -d azathoth.local -e local -n azathoth.local
+harness-deployment . -u -dtls -l -d azathoth.local -e local -n azathoth
----
In the state of the repository I have on my machine, the apps and services that will be deployed and that `harness-deployment` generated the configuration for are:
@@ -307,6 +313,8 @@ The file tree should now be the following.
Now you can build/deploy/run it using `skaffold`.
+Before running `skaffold run` go inside the newly created application using harness-application; and make sure the frontend for the application contains package-lock.json. If not then install the dependencies by running `npm i --legacy-peer-deps`.
+
.Building/deploying/running the webapp with skaffold
[source,bash]
----
diff --git a/libraries/cloudharness-common/cloudharness/auth/keycloak.py b/libraries/cloudharness-common/cloudharness/auth/keycloak.py
index eb51fb15..c86aed3a 100644
--- a/libraries/cloudharness-common/cloudharness/auth/keycloak.py
+++ b/libraries/cloudharness-common/cloudharness/auth/keycloak.py
@@ -442,13 +442,19 @@ def get_user(self, user_id, with_details=False) -> User:
raise UserNotFound(user_id)
except InvalidToken as e:
raise UserNotFound(user_id)
+
else:
- found_users = admin_client.get_users({"username": user_id})
+ found_users = admin_client.get_users({"username": user_id, "exact": True})
if len(found_users) == 0:
raise UserNotFound(user_id)
- user = admin_client.get_user(found_users[0]['id']) # Load full data
-
+ try:
+ user = admin_client.get_user(found_users[0]['id']) # Load full data
+ except KeycloakGetError as e:
+ raise UserNotFound(user_id)
+ except InvalidToken as e:
+ raise UserNotFound(user_id)
+
user.update({
"userGroups": admin_client.get_user_groups(user_id=user['id'], brief_representation=not with_details),
'realmRoles': admin_client.get_realm_roles_of_user(user['id'])
diff --git a/libraries/cloudharness-common/cloudharness/auth/quota.py b/libraries/cloudharness-common/cloudharness/auth/quota.py
index a4106ee8..00137ef4 100644
--- a/libraries/cloudharness-common/cloudharness/auth/quota.py
+++ b/libraries/cloudharness-common/cloudharness/auth/quota.py
@@ -1,3 +1,4 @@
+import re
from keycloak import KeycloakError
from .keycloak import AuthClient
from cloudharness.applications import get_current_configuration
@@ -93,10 +94,12 @@ def _compute_quotas_from_tree(node: QuotaNode):
child_attrs = _compute_quotas_from_tree(child)
for key in child_attrs:
try:
- child_val = float(child_attrs[key])
+ # we expect all quota values to be numbers: the unit is implicit and
+ # defined at usage time
+ child_val = attribute_to_quota(child_attrs[key])
except:
- # value not a float, skip (use 0)
- child_val = 0
+ # value not a float, skip
+ continue
if not key in new_attrs or new_attrs[key] < child_val:
new_attrs.update({key: child_val})
for key in new_attrs:
@@ -104,6 +107,10 @@ def _compute_quotas_from_tree(node: QuotaNode):
return node.attrs
+def attribute_to_quota(attr_value: str):
+ return float(re.sub("[^0-9.]", "", attr_value) if type(attr_value) is str else attr_value)
+
+
def get_user_quotas(application_config: ApplicationConfig = None, user_id: str = None) -> dict:
"""Get the user quota from Keycloak and application
@@ -142,5 +149,5 @@ def get_user_quotas(application_config: ApplicationConfig = None, user_id: str =
user_quotas.update({key: group_quotas[key]})
for key in base_quotas:
if key not in user_quotas:
- user_quotas.update({key: base_quotas[key]})
+ user_quotas.update({key: attribute_to_quota(base_quotas[key])})
return user_quotas
diff --git a/libraries/models/api/openapi.yaml b/libraries/models/api/openapi.yaml
index a2d8400e..577bff53 100644
--- a/libraries/models/api/openapi.yaml
+++ b/libraries/models/api/openapi.yaml
@@ -1,839 +1,848 @@
----
openapi: 3.0.2
info:
- title: cloudharness
- version: 1.0.0
+ title: cloudharness
+ version: 1.0.0
components:
- schemas:
- AutoArtifactSpec:
- description: ""
- required:
- - auto
- type: object
- properties:
- auto:
- description: "When true, enables automatic template"
- type: boolean
- name:
- description: ""
- type: string
- UriRoleMappingConfig:
- description:
- "Defines the application Gatekeeper configuration, if enabled (i.e.\
- \ `secured: true`."
- required:
- - roles
- - uri
- type: object
- properties:
- uri:
- $ref: "#/components/schemas/PathSpecifier"
- description: Path to secure
- roles:
- description: Roles allowed to access the present uri
- type: array
- items:
- type: string
- ServiceAutoArtifactConfig:
- description: ""
- type: object
- allOf:
- - type: object
- properties:
- port:
- description: Service port
- type: integer
- - $ref: "#/components/schemas/AutoArtifactSpec"
- ApplicationDependenciesConfig:
- description: ""
- type: object
- properties:
- hard:
- description:
- Hard dependencies indicate that the application may not start
- without these other applications.
- type: array
- items:
- type: string
- soft:
- description:
- Soft dependencies indicate that the application will work partially
- without these other applications.
- type: array
- items:
- type: string
- build:
- description:
- Hard dependencies indicate that the application Docker image
- build requires these base/common images
- type: array
- items:
+ schemas:
+ AutoArtifactSpec:
+ description: ''
+ required:
+ - auto
+ type: object
+ properties:
+ auto:
+ description: 'When true, enables automatic template'
+ type: boolean
+ name:
+ description: ''
+ type: string
+ UriRoleMappingConfig:
+ description: 'Defines the application Gatekeeper configuration, if enabled (i.e. `secured: true`.'
+ required:
+ - roles
+ - uri
+ type: object
+ properties:
+ uri:
+ $ref: '#/components/schemas/PathSpecifier'
+ description: Path to secure
+ roles:
+ description: Roles allowed to access the present uri
+ type: array
+ items:
+ type: string
+ ServiceAutoArtifactConfig:
+ description: ''
+ type: object
+ allOf:
+ -
+ type: object
+ properties:
+ port:
+ description: Service port
+ type: integer
+ -
+ $ref: '#/components/schemas/AutoArtifactSpec'
+ ApplicationDependenciesConfig:
+ description: ''
+ type: object
+ properties:
+ hard:
+ description: >-
+ Hard dependencies indicate that the application may not start without these other
+ applications.
+ type: array
+ items:
+ type: string
+ soft:
+ description: >-
+ Soft dependencies indicate that the application will work partially without these
+ other applications.
+ type: array
+ items:
+ type: string
+ build:
+ description: >-
+ Hard dependencies indicate that the application Docker image build requires these
+ base/common images
+ type: array
+ items:
+ type: string
+ DeploymentResourcesConf:
+ description: ''
+ type: object
+ properties:
+ requests:
+ $ref: '#/components/schemas/CpuMemoryConfig'
+ description: ''
+ limits:
+ $ref: '#/components/schemas/CpuMemoryConfig'
+ description: ''
+ CpuMemoryConfig:
+ description: ''
+ type: object
+ properties:
+ cpu:
+ description: ''
+ type: string
+ memory:
+ description: ''
+ type: string
+ FileResourcesConfig:
+ description: ''
+ required:
+ - name
+ - src
+ - dst
+ type: object
+ properties:
+ name:
+ $ref: '#/components/schemas/Filename'
+ description: ''
+ src:
+ $ref: '#/components/schemas/Filename'
+ description: ''
+ dst:
+ description: ''
+ type: string
+ ApplicationProbe:
+ description: >-
+ Define a Kubernetes probe See also the
+
+ [official
+ documentation](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/)
+ required:
+ - path
+ type: object
+ properties:
+ path:
+ $ref: '#/components/schemas/URL'
+ description: ''
+ periodSeconds:
+ description: ''
+ type: number
+ failureThreshold:
+ description: ''
+ type: number
+ initialDelaySeconds:
+ description: ''
+ type: number
+ URL:
+ description: ''
type: string
- DeploymentResourcesConf:
- description: ""
- type: object
- properties:
- requests:
- $ref: "#/components/schemas/CpuMemoryConfig"
- description: ""
- limits:
- $ref: "#/components/schemas/CpuMemoryConfig"
- description: ""
- CpuMemoryConfig:
- description: ""
- type: object
- properties:
- cpu:
- description: ""
- type: string
- memory:
- description: ""
- type: string
- FileResourcesConfig:
- description: ""
- required:
- - name
- - src
- - dst
- type: object
- properties:
- name:
- $ref: "#/components/schemas/Filename"
- description: ""
- src:
- $ref: "#/components/schemas/Filename"
- description: ""
- dst:
- description: ""
- type: string
- ApplicationProbe:
- description: |-
- Define a Kubernetes probe See also the
- [official documentation](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/)
- required:
- - path
- type: object
- properties:
- path:
- $ref: "#/components/schemas/URL"
- description: ""
- periodSeconds:
- description: ""
- type: number
- failureThreshold:
- description: ""
- type: number
- initialDelaySeconds:
- description: ""
- type: number
- URL:
- description: ""
- type: string
- ApplicationConfig:
- description: Place here the values to configure your application helm templates.
- required:
- - harness
- type: object
- properties:
- harness:
- $ref: "#/components/schemas/ApplicationHarnessConfig"
- description:
- Values inside this section have a special meaning to cloudharness
- (e.g. enabling and configuring automatic deployment)
- additionalProperties: true
- HarnessMainConfig:
- description: ""
- required:
- - local
- - secured_gatekeepers
- - domain
- - namespace
- - mainapp
- - apps
- type: object
- properties:
- local:
- description: "If set to true, local DNS mapping is added to pods."
- type: boolean
- secured_gatekeepers:
- description:
- Enables/disables Gatekeepers on secured applications. Set to
- false for testing/development
- type: boolean
- domain:
- description: The root domain
- type: string
- example: The root domain.
- namespace:
- description: The K8s namespace.
- type: string
- mainapp:
- description: Defines the app to map to the root domain
- type: string
- registry:
- $ref: "#/components/schemas/RegistryConfig"
- description: ""
- tag:
- description: Docker tag used to push/pull the built images.
- type: string
- apps:
- $ref: "#/components/schemas/ApplicationsConfigsMap"
- description: ""
- env:
- description: Environmental variables added to all pods
- type: array
- items:
- $ref: "#/components/schemas/NameValue"
- privenv:
- $ref: "#/components/schemas/NameValue"
- description: Private environmental variables added to all pods
- backup:
- $ref: "#/components/schemas/BackupConfig"
- description: ""
- name:
- description: Base name
- type: string
- task-images:
- $ref: "#/components/schemas/SimpleMap"
- description: ""
- RegistryConfig:
- description: ""
- required:
- - name
- type: object
- properties:
- name:
- $ref: "#/components/schemas/URL"
- description: The docker registry where built images are pushed
- secret:
- description: Optional secret used for pulling from docker registry.
- type: string
- SimpleMap:
- description: ""
- type: object
- additionalProperties:
- type: string
- FreeObject:
- description: ""
- type: object
- additionalProperties: true
- DatabaseDeploymentConfig:
- description: ""
- type: object
- allOf:
- - type: object
- properties:
- type:
- description: |-
- Define the database type.
+ ApplicationConfig:
+ description: Place here the values to configure your application helm templates.
+ required:
+ - harness
+ type: object
+ properties:
+ harness:
+ $ref: '#/components/schemas/ApplicationHarnessConfig'
+ description: >-
+ Values inside this section have a special meaning to cloudharness (e.g. enabling and
+ configuring automatic deployment)
+ additionalProperties: true
+ RegistryConfig:
+ description: ''
+ required:
+ - name
+ type: object
+ properties:
+ name:
+ $ref: '#/components/schemas/URL'
+ description: The docker registry where built images are pushed
+ secret:
+ description: Optional secret used for pulling from docker registry.
+ type: string
+ FreeObject:
+ description: ''
+ type: object
+ additionalProperties: true
+ DatabaseDeploymentConfig:
+ description: ''
+ type: object
+ allOf:
+ -
+ type: object
+ properties:
+ type:
+ description: |-
+ Define the database type.
- One of (mongo, postgres, neo4j, sqlite3)
- pattern: ^(mongo|postgres|neo4j|sqlite3)$
- type: string
- example: '"neo4j"'
- size:
- description: Specify database disk size
- type: string
- example: 1Gi
- user:
- description: database username
- type: string
- pass:
- format: password
- description: Database password
- type: string
- image_ref:
- description: Used for referencing images from the build
- type: string
- example: "image_ref: myownpgimage"
- mongo:
- $ref: "#/components/schemas/FreeObject"
- description: Mongo db specific configuration
- postgres:
- $ref: "#/components/schemas/FreeObject"
- description: Postgres database specific configuration
- neo4j:
- description: Neo4j database specific configuration
- resources:
- $ref: "#/components/schemas/DeploymentResourcesConf"
- description: Database deployment resources
- - $ref: "#/components/schemas/AutoArtifactSpec"
- ApplicationsConfigsMap:
- description: ""
- type: object
- additionalProperties:
- $ref: "#/components/schemas/ApplicationConfig"
- NameValue:
- description: ""
- required:
- - name
- type: object
- properties:
- name:
- description: ""
- type: string
- value:
- description: ""
- type: string
- IngressConfig:
- description: ""
- type: object
- allOf:
- - type: object
- properties:
- ssl_redirect:
- description: ""
- type: boolean
- letsencrypt:
- description: ""
- type: object
- properties:
+ One of (mongo, postgres, neo4j, sqlite3)
+ pattern: ^(mongo|postgres|neo4j|sqlite3)$
+ type: string
+ example: '"neo4j"'
+ size:
+ description: Specify database disk size
+ type: string
+ example: 1Gi
+ user:
+ description: database username
+ type: string
+ pass:
+ format: password
+ description: Database password
+ type: string
+ image_ref:
+ description: Used for referencing images from the build
+ type: string
+ example: 'image_ref: myownpgimage'
+ mongo:
+ $ref: '#/components/schemas/FreeObject'
+ description: Mongo db specific configuration
+ postgres:
+ $ref: '#/components/schemas/FreeObject'
+ description: Postgres database specific configuration
+ neo4j:
+ description: Neo4j database specific configuration
+ resources:
+ $ref: '#/components/schemas/DeploymentResourcesConf'
+ description: Database deployment resources
+ -
+ $ref: '#/components/schemas/AutoArtifactSpec'
+ ApplicationsConfigsMap:
+ description: ''
+ type: object
+ additionalProperties:
+ $ref: '#/components/schemas/ApplicationConfig'
+ NameValue:
+ description: ''
+ required:
+ - name
+ type: object
+ properties:
+ name:
+ description: ''
+ type: string
+ value:
+ description: ''
+ type: string
+ IngressConfig:
+ description: ''
+ type: object
+ allOf:
+ -
+ type: object
+ properties:
+ ssl_redirect:
+ description: ''
+ type: boolean
+ letsencrypt:
+ description: ''
+ type: object
+ properties:
+ email:
+ type: string
+ -
+ $ref: '#/components/schemas/AutoArtifactSpec'
+ BackupConfig:
+ description: ''
+ required:
+ - dir
+ - resources
+ type: object
+ properties:
+ active:
+ description: ''
+ type: boolean
+ keep_days:
+ description: ''
+ type: integer
+ keep_weeks:
+ description: ''
+ type: integer
+ keep_months:
+ description: ''
+ type: integer
+ schedule:
+ description: Cron expression
+ pattern: >-
+ /(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every
+ (\d+(ns|us|µs|ms|s|m|h))+)|((((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*) ?){5,7})/
+ type: string
+ suffix:
+ description: The file suffix added to backup files
+ volumesize:
+ description: The volume size for backups (all backups share the same volume)
+ type: string
+ dir:
+ $ref: '#/components/schemas/Filename'
+ description: 'Target directory of backups, the mount point of the persistent volume.'
+ resources:
+ $ref: '#/components/schemas/DeploymentResourcesConf'
+ description: ''
+ UserGroup:
+ type: object
+ properties:
+ access:
+ type: object
+ additionalProperties: true
+ attributes:
+ $ref: '#/components/schemas/SimpleMap'
+ additionalProperties: true
+ clientRoles:
+ type: object
+ additionalProperties: true
+ id:
+ type: string
+ name:
+ type: string
+ path:
+ type: string
+ realmRoles:
+ type: array
+ items:
+ type: string
+ subGroups:
+ type: array
+ items:
+ $ref: '#/components/schemas/UserGroup'
+ UserCredential:
+ type: object
+ properties:
+ createdDate:
+ format: int64
+ type: integer
+ credentialData:
+ type: string
+ id:
+ type: string
+ priority:
+ format: int32
+ type: integer
+ secretData:
+ type: string
+ temporary:
+ type: boolean
+ type:
+ type: string
+ userLabel:
+ type: string
+ value:
+ type: string
+ User:
+ type: object
+ properties:
+ access:
+ type: object
+ additionalProperties: true
+ attributes:
+ type: object
+ additionalProperties: true
+ clientRoles:
+ type: object
+ additionalProperties: true
+ createdTimestamp:
+ format: int64
+ type: integer
+ credentials:
+ type: array
+ items:
+ $ref: '#/components/schemas/UserCredential'
+ disableableCredentialTypes:
+ type: array
+ items:
+ type: string
email:
- type: string
- - $ref: "#/components/schemas/AutoArtifactSpec"
- BackupConfig:
- description: ""
- required:
- - dir
- - resources
- type: object
- properties:
- active:
- description: ""
- type: boolean
- keep_days:
- description: ""
- type: integer
- keep_weeks:
- description: ""
- type: integer
- keep_months:
- description: ""
- type: integer
- schedule:
- description: Cron expression
- pattern:
- "/(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every\
- \ (\\d+(ns|us|µs|ms|s|m|h))+)|((((\\d+,)+\\d+|(\\d+(\\/|-)\\d+)|\\d+|\\\
- *) ?){5,7})/"
- type: string
- suffix:
- description: The file suffix added to backup files
- volumesize:
- description: The volume size for backups (all backups share the same volume)
- type: string
- dir:
- $ref: "#/components/schemas/Filename"
- description:
- "Target directory of backups, the mount point of the persistent\
- \ volume."
- resources:
- $ref: "#/components/schemas/DeploymentResourcesConf"
- description: ""
- Quota:
- description: ""
- type: object
- additionalProperties:
- type: string
- example:
- quota-ws-max: 5
- quota-storage-max: 1G
- UserGroup:
- type: object
- properties:
- access:
- type: object
- additionalProperties: true
- attributes:
- $ref: "#/components/schemas/SimpleMap"
- additionalProperties: true
- clientRoles:
- type: object
- additionalProperties: true
- id:
- type: string
- name:
- type: string
- path:
- type: string
- realmRoles:
- type: array
- items:
- type: string
- subGroups:
- type: array
- items:
- $ref: "#/components/schemas/UserGroup"
- UserCredential:
- type: object
- properties:
- createdDate:
- format: int64
- type: integer
- credentialData:
- type: string
- id:
- type: string
- priority:
- format: int32
- type: integer
- secretData:
- type: string
- temporary:
- type: boolean
- type:
- type: string
- userLabel:
- type: string
- value:
- type: string
- User:
- type: object
- properties:
- access:
- type: object
- additionalProperties: true
- attributes:
- type: object
- additionalProperties: true
- clientRoles:
- type: object
- additionalProperties: true
- createdTimestamp:
- format: int64
- type: integer
- credentials:
- type: array
- items:
- $ref: "#/components/schemas/UserCredential"
- disableableCredentialTypes:
- type: array
- items:
- type: string
- email:
- type: string
- emailVerified:
- type: boolean
- enabled:
- type: boolean
- federationLink:
- type: string
- firstName:
- type: string
- groups:
- type: array
- items:
- type: string
- id:
- type: string
- lastName:
- type: string
- realmRoles:
- type: array
- items:
+ type: string
+ emailVerified:
+ type: boolean
+ enabled:
+ type: boolean
+ federationLink:
+ type: string
+ firstName:
+ type: string
+ groups:
+ type: array
+ items:
+ type: string
+ id:
+ type: string
+ lastName:
+ type: string
+ realmRoles:
+ type: array
+ items:
+ type: string
+ requiredActions:
+ type: array
+ items:
+ type: string
+ serviceAccountClientId:
+ type: string
+ username:
+ type: string
+ additionalProperties: {}
+ Filename:
+ description: ''
+ pattern: '^[^<>:;,?*|]+$'
type: string
- requiredActions:
- type: array
- items:
+ PathSpecifier:
+ description: ''
+ pattern: '^[^<>:;,?|]+$'
type: string
- serviceAccountClientId:
- type: string
- username:
- type: string
- additionalProperties: {}
- Filename:
- description: ""
- pattern: "^[^<>:;,?*|]+$"
- type: string
- PathSpecifier:
- description: ""
- pattern: "^[^<>:;,?|]+$"
- type: string
- CDCEvent:
- description: |-
- A message sent to the orchestration queue.
- Applications can listen to these events to react to data change events happening
- on other applications.
- required:
- - message_type
- - operation
- - uid
- - meta
- type: object
- properties:
- operation:
- description: the operation on the object e.g. create / update / delete
- enum:
- - create
- - update
- - delete
- - other
- type: string
- uid:
- description: the unique identifier attribute of the object
- type: string
- message_type:
- description: the type of the message (relates to the object type) e.g. jobs
- type: string
- resource:
- $ref: "#/components/schemas/FreeObject"
- description: The target object
- meta:
- $ref: "#/components/schemas/CDCEventMeta"
- description: ""
- CDCEventMeta:
- description: ""
- required:
- - app_name
- type: object
- properties:
- app_name:
- description: The name of the application/microservice sending the message
- type: string
- user:
- $ref: "#/components/schemas/User"
- description: ""
- args:
- description: the caller function arguments
- type: array
- items:
- $ref: "#/components/schemas/FreeObject"
- kwargs:
- description: the caller function keyword arguments
- description:
- description: General description -- for human consumption
- type: string
- ApplicationHarnessConfig:
- description:
- "Define helm variables that allow CloudHarness to enable and configure\
- \ your \napplication's deployment"
- required: []
- type: object
- properties:
- deployment:
- $ref: "#/components/schemas/DeploymentAutoArtifactConfig"
- description:
- Defines reference deployment parameters. Values maps to k8s
- spec
- service:
- $ref: "#/components/schemas/ServiceAutoArtifactConfig"
- description: Defines automatic service parameters.
- subdomain:
- description: "If specified, an ingress will be created at [subdomain].[.Values.domain]"
- type: string
- aliases:
- description:
- "If specified, an ingress will be created at [alias].[.Values.domain]\
- \ for each alias"
- type: array
- items:
- type: string
- domain:
- description: "If specified, an ingress will be created at [domain]"
- type: string
- dependencies:
- $ref: "#/components/schemas/ApplicationDependenciesConfig"
- description:
- Application dependencies are used to define what is required
- in the deployment when --include (-i) is used. Specify application names
- in the list.
- secured:
- description: "When true, the application is shielded with a getekeeper"
- type: boolean
- uri_role_mapping:
- description:
- "Map uri/roles to secure with the Gatekeeper (if `secured:\
- \ true`)"
- type: array
- items:
- $ref: "#/components/schemas/UriRoleMappingConfig"
- secrets:
- $ref: "#/components/schemas/SimpleMap"
- description: |-
- Define secrets will be mounted in the deployment
+ CDCEvent:
+ description: |-
+ A message sent to the orchestration queue.
+ Applications can listen to these events to react to data change events happening
+ on other applications.
+ required:
+ - message_type
+ - operation
+ - uid
+ - meta
+ type: object
+ properties:
+ operation:
+ description: the operation on the object e.g. create / update / delete
+ enum:
+ - create
+ - update
+ - delete
+ - other
+ type: string
+ uid:
+ description: the unique identifier attribute of the object
+ type: string
+ message_type:
+ description: the type of the message (relates to the object type) e.g. jobs
+ type: string
+ resource:
+ $ref: '#/components/schemas/FreeObject'
+ description: The target object
+ meta:
+ $ref: '#/components/schemas/CDCEventMeta'
+ description: ''
+ CDCEventMeta:
+ description: ''
+ required:
+ - app_name
+ type: object
+ properties:
+ app_name:
+ description: The name of the application/microservice sending the message
+ type: string
+ user:
+ $ref: '#/components/schemas/User'
+ description: ''
+ args:
+ description: the caller function arguments
+ type: array
+ items:
+ $ref: '#/components/schemas/FreeObject'
+ kwargs:
+ description: the caller function keyword arguments
+ description:
+ description: General description -- for human consumption
+ type: string
+ ApplicationHarnessConfig:
+ description: |-
+ Define helm variables that allow CloudHarness to enable and configure your
+ application's deployment
+ required: []
+ type: object
+ properties:
+ deployment:
+ $ref: '#/components/schemas/DeploymentAutoArtifactConfig'
+ description: Defines reference deployment parameters. Values maps to k8s spec
+ service:
+ $ref: '#/components/schemas/ServiceAutoArtifactConfig'
+ description: Defines automatic service parameters.
+ subdomain:
+ description: 'If specified, an ingress will be created at [subdomain].[.Values.domain]'
+ type: string
+ aliases:
+ description: 'If specified, an ingress will be created at [alias].[.Values.domain] for each alias'
+ type: array
+ items:
+ type: string
+ domain:
+ description: 'If specified, an ingress will be created at [domain]'
+ type: string
+ dependencies:
+ $ref: '#/components/schemas/ApplicationDependenciesConfig'
+ description: >-
+ Application dependencies are used to define what is required in the deployment when
+ --include (-i) is used. Specify application names in the list.
+ secured:
+ description: 'When true, the application is shielded with a getekeeper'
+ type: boolean
+ uri_role_mapping:
+ description: 'Map uri/roles to secure with the Gatekeeper (if `secured: true`)'
+ type: array
+ items:
+ $ref: '#/components/schemas/UriRoleMappingConfig'
+ secrets:
+ $ref: '#/components/schemas/SimpleMap'
+ description: |-
+ Define secrets will be mounted in the deployment
- Define as
+ Define as
- ```yaml
- secrets:
- secret_name: 'value'
+ ```yaml
+ secrets:
+ secret_name: 'value'
- ```
+ ```
- Values if left empty are randomly generated
- use_services:
- description:
- "Specify which services this application uses in the frontend\
- \ to create proxy ingresses. e.g. \n```\n- name: samples\n```"
- type: array
- items:
- type: string
- database:
- $ref: "#/components/schemas/DatabaseDeploymentConfig"
- description: ""
- resources:
- description: |-
- Application file resources. Maps from deploy/resources folder and mounts as
- configmaps
- type: array
- items:
- $ref: "#/components/schemas/FileResourcesConfig"
- readinessProbe:
- $ref: "#/components/schemas/ApplicationProbe"
- description: Kubernetes readiness probe configuration
- startupProbe:
- $ref: "#/components/schemas/ApplicationProbe"
- description: ""
- livenessProbe:
- $ref: "#/components/schemas/ApplicationProbe"
- description: Kubernetes liveness probe configuration
- sourceRoot:
- $ref: "#/components/schemas/Filename"
- description: ""
- name:
- description: |-
- Application's name. Do not edit, the value is automatically set from the
- application directory's name
- type: string
- jupyterhub:
- $ref: "#/components/schemas/JupyterHubConfig"
- description: |
- Configurations specific to jupyterhub. Edit only if your application is
- configured as a jupyterhub deployment
- accounts:
- $ref: "#/components/schemas/ApplicationAccountsConfig"
- description: Define specific test users and roles for this application
- test:
- $ref: "#/components/schemas/ApplicationTestConfig"
- description: Enable and configure automated testing for this application.
- additionalProperties: true
- JupyterHubConfig:
- description: ""
- type: object
- properties:
- args:
- description: arguments passed to the container
- type: array
- items:
- type: string
- extraConfig:
- $ref: "#/components/schemas/SimpleMap"
- description:
- allows you to add Python snippets to the jupyterhub_config.py
- file
- spawnerExtraConfig:
- $ref: "#/components/schemas/FreeObject"
- description:
- allows you to add values to the spawner object without the
- need of creating a new hook
- applicationHook:
- description:
- "change the hook function (advanced)\n\nSpecify the Python\
- \ name of the function (full module path, the module must be \ninstalled\
- \ in the Docker image)"
- example: my_lib.change_pod_manifest
- additionalProperties: true
- UserRole:
- type: object
- properties:
- attributes:
- type: object
- additionalProperties: true
- clientRole:
- type: boolean
- composite:
- type: boolean
- containerId:
- type: string
- description:
- type: string
- id:
- type: string
- name:
- type: string
- additionalProperties: true
- ApplicationAccountsConfig:
- description: ""
- type: object
- properties:
- roles:
- description:
- Specify roles to be created in this deployment specific for
- this application
- type: array
- items:
- type: string
- users:
- description:
- "Defines test users to be added to the deployment, specific\
- \ for this application"
- type: array
- items:
- $ref: "#/components/schemas/ApplicationUser"
- ApplicationUser:
- description: Defines a user
- required:
- - username
- type: object
- properties:
- username:
- description: ""
- type: string
- password:
- format: password
- description: ""
- type: string
- clientRoles:
- description: ""
- type: array
- items:
- type: string
- realmRoles:
- description: ""
- type: array
- items:
- type: string
- ApplicationTestConfig:
- description: ""
- required:
- - unit
- - e2e
- - api
- type: object
- properties:
- unit:
- $ref: "#/components/schemas/UnitTestsConfig"
- description: ""
- api:
- $ref: "#/components/schemas/ApiTestsConfig"
- description: ""
- e2e:
- $ref: "#/components/schemas/E2ETestsConfig"
- description: ""
- UnitTestsConfig:
- description: ""
- required:
- - enabled
- - commands
- type: object
- properties:
- enabled:
- description: "Enables unit tests for this application (default: true)"
- type: boolean
- commands:
- description: Commands to run unit tests
- type: array
- items:
- type: string
- example: '["pytest /usr/src/app/samples/test"]'
- E2ETestsConfig:
- description: ""
- required:
- - enabled
- - smoketest
- type: object
- properties:
- enabled:
- description:
- "Enables end to end testing for this application (default:\
- \ false)"
- type: boolean
- smoketest:
- description: Specify whether to run the common smoke tests
- type: boolean
- ignoreConsoleErrors:
- description: ""
- type: boolean
- ignoreRequestErrors:
- description: ""
- type: boolean
- ApiTestsConfig:
- description: ""
- required:
- - enabled
- - autotest
- - checks
- type: object
- properties:
- enabled:
- description: "Enables api tests for this application (default: false)"
- type: boolean
- autotest:
- description: Specify whether to run the common smoke tests
- type: boolean
- runParams:
- description: Additional schemathesis parameters
- type: array
- items:
- type: string
- checks:
- description: |-
- One of the Schemathesis checks:
+ Values if left empty are randomly generated
+ use_services:
+ description: >-
+ Specify which services this application uses in the frontend to create proxy
+ ingresses. e.g.
- - not_a_server_error. The response has 5xx HTTP status;
- - status_code_conformance. The response status is not defined in the API schema;
- - content_type_conformance. The response content type is not defined in the API schema;
- - response_schema_conformance. The response content does not conform to the schema defined for this specific response;
- - response_headers_conformance. The response headers does not contain all defined headers.
- type: array
- items:
- type: string
- example: '["not_a_server_error", "status_code_conformance"]'
- DeploymentAutoArtifactConfig:
- description: ""
- type: object
- allOf:
- - type: object
- properties:
- port:
- description: Deployment port
- type: string
- replicas:
- description: Number of replicas
- type: integer
- image:
- description: |-
- Image name to use in the deployment. Leave it blank to set from the application's
- Docker file
- pattern: "(?:[a-z]+/)?([a-z]+)(?::[0-9]+)?"
- type: string
- resources:
- $ref: "#/components/schemas/DeploymentResourcesConf"
- description: Deployment resources
- volume:
- $ref: "#/components/schemas/DeploymentVolumeSpec"
- description: Volume specification
- resources:
- description: Deployment resources
- type: string
- test:
- description: ssaa
- type: string
- - $ref: "#/components/schemas/AutoArtifactSpec"
- DeploymentVolumeSpec:
- description: |-
- Defines a volume attached to the deployment.
- Automatically created the volume claim and mounts.
- type: object
- allOf:
- - required:
- - mountpath
- type: object
- properties:
- mountpath:
- description: The mount path for the volume
- type: string
- size:
- description: "The volume size. \n\nE.g. 5Gi"
- usenfs:
- description:
- Set to `true` to use the nfs on the created volume and mount
- as ReadWriteMany.
- type: boolean
- - $ref: "#/components/schemas/AutoArtifactSpec"
- example:
- auto: true
- mountpath: /usr/src/app/persistent
- name: my-files
- size: 5Gi
- usenfs: true
+ ```
+
+ - name: samples
+
+ ```
+ type: array
+ items:
+ type: string
+ database:
+ $ref: '#/components/schemas/DatabaseDeploymentConfig'
+ description: ''
+ resources:
+ description: |-
+ Application file resources. Maps from deploy/resources folder and mounts as
+ configmaps
+ type: array
+ items:
+ $ref: '#/components/schemas/FileResourcesConfig'
+ readinessProbe:
+ $ref: '#/components/schemas/ApplicationProbe'
+ description: Kubernetes readiness probe configuration
+ startupProbe:
+ $ref: '#/components/schemas/ApplicationProbe'
+ description: ''
+ livenessProbe:
+ $ref: '#/components/schemas/ApplicationProbe'
+ description: Kubernetes liveness probe configuration
+ sourceRoot:
+ $ref: '#/components/schemas/Filename'
+ description: ''
+ name:
+ description: |-
+ Application's name. Do not edit, the value is automatically set from the
+ application directory's name
+ type: string
+ jupyterhub:
+ $ref: '#/components/schemas/JupyterHubConfig'
+ description: |
+ Configurations specific to jupyterhub. Edit only if your application is
+ configured as a jupyterhub deployment
+ accounts:
+ $ref: '#/components/schemas/ApplicationAccountsConfig'
+ description: Define specific test users and roles for this application
+ test:
+ $ref: '#/components/schemas/ApplicationTestConfig'
+ description: Enable and configure automated testing for this application.
+ quotas:
+ $ref: '#/components/schemas/Quota'
+ description: ''
+ additionalProperties: true
+ JupyterHubConfig:
+ description: ''
+ type: object
+ properties:
+ args:
+ description: arguments passed to the container
+ type: array
+ items:
+ type: string
+ extraConfig:
+ $ref: '#/components/schemas/SimpleMap'
+ description: allows you to add Python snippets to the jupyterhub_config.py file
+ spawnerExtraConfig:
+ $ref: '#/components/schemas/FreeObject'
+ description: allows you to add values to the spawner object without the need of creating a new hook
+ applicationHook:
+ description: |-
+ change the hook function (advanced)
+
+ Specify the Python name of the function (full module path, the module must be
+ installed in the Docker image)
+ example: my_lib.change_pod_manifest
+ additionalProperties: true
+ UserRole:
+ type: object
+ properties:
+ attributes:
+ type: object
+ additionalProperties: true
+ clientRole:
+ type: boolean
+ composite:
+ type: boolean
+ containerId:
+ type: string
+ description:
+ type: string
+ id:
+ type: string
+ name:
+ type: string
+ additionalProperties: true
+ ApplicationAccountsConfig:
+ description: ''
+ type: object
+ properties:
+ roles:
+ description: Specify roles to be created in this deployment specific for this application
+ type: array
+ items:
+ type: string
+ users:
+ description: 'Defines test users to be added to the deployment, specific for this application'
+ type: array
+ items:
+ $ref: '#/components/schemas/ApplicationUser'
+ ApplicationUser:
+ description: Defines a user
+ required:
+ - username
+ type: object
+ properties:
+ username:
+ description: ''
+ type: string
+ password:
+ format: password
+ description: ''
+ type: string
+ clientRoles:
+ description: ''
+ type: array
+ items:
+ type: string
+ realmRoles:
+ description: ''
+ type: array
+ items:
+ type: string
+ ApplicationTestConfig:
+ description: ''
+ required:
+ - unit
+ - e2e
+ - api
+ type: object
+ properties:
+ unit:
+ $ref: '#/components/schemas/UnitTestsConfig'
+ description: ''
+ api:
+ $ref: '#/components/schemas/ApiTestsConfig'
+ description: ''
+ e2e:
+ $ref: '#/components/schemas/E2ETestsConfig'
+ description: ''
+ UnitTestsConfig:
+ description: ''
+ required:
+ - enabled
+ - commands
+ type: object
+ properties:
+ enabled:
+ description: 'Enables unit tests for this application (default: true)'
+ type: boolean
+ commands:
+ description: Commands to run unit tests
+ type: array
+ items:
+ type: string
+ example: '["pytest /usr/src/app/samples/test"]'
+ E2ETestsConfig:
+ description: ''
+ required:
+ - enabled
+ - smoketest
+ type: object
+ properties:
+ enabled:
+ description: 'Enables end to end testing for this application (default: false)'
+ type: boolean
+ smoketest:
+ description: Specify whether to run the common smoke tests
+ type: boolean
+ ignoreConsoleErrors:
+ description: ''
+ type: boolean
+ ignoreRequestErrors:
+ description: ''
+ type: boolean
+ ApiTestsConfig:
+ description: ''
+ required:
+ - enabled
+ - autotest
+ - checks
+ type: object
+ properties:
+ enabled:
+ description: 'Enables api tests for this application (default: false)'
+ type: boolean
+ autotest:
+ description: Specify whether to run the common smoke tests
+ type: boolean
+ runParams:
+ description: Additional schemathesis parameters
+ type: array
+ items:
+ type: string
+ checks:
+ description: >-
+ One of the Schemathesis checks:
+
+
+ - not_a_server_error. The response has 5xx HTTP status;
+
+ - status_code_conformance. The response status is not defined in the API schema;
+
+ - content_type_conformance. The response content type is not defined in the API
+ schema;
+
+ - response_schema_conformance. The response content does not conform to the schema
+ defined for this specific response;
+
+ - response_headers_conformance. The response headers does not contain all defined
+ headers.
+ type: array
+ items:
+ type: string
+ example: '["not_a_server_error", "status_code_conformance"]'
+ DeploymentAutoArtifactConfig:
+ description: ''
+ type: object
+ allOf:
+ -
+ type: object
+ properties:
+ port:
+ description: Deployment port
+ type: string
+ replicas:
+ description: Number of replicas
+ type: integer
+ image:
+ description: >-
+ Image name to use in the deployment. Leave it blank to set from the
+ application's
+
+ Docker file
+ pattern: '(?:[a-z]+/)?([a-z]+)(?::[0-9]+)?'
+ type: string
+ resources:
+ $ref: '#/components/schemas/DeploymentResourcesConf'
+ description: Deployment resources
+ volume:
+ $ref: '#/components/schemas/DeploymentVolumeSpec'
+ description: Volume specification
+ resources:
+ description: Deployment resources
+ type: string
+ test:
+ description: ssaa
+ type: string
+ -
+ $ref: '#/components/schemas/AutoArtifactSpec'
+ DeploymentVolumeSpec:
+ description: |-
+ Defines a volume attached to the deployment.
+ Automatically created the volume claim and mounts.
+ type: object
+ allOf:
+ -
+ required:
+ - mountpath
+ type: object
+ properties:
+ mountpath:
+ description: The mount path for the volume
+ type: string
+ size:
+ description: |-
+ The volume size.
+
+ E.g. 5Gi
+ usenfs:
+ description: Set to `true` to use the nfs on the created volume and mount as ReadWriteMany.
+ type: boolean
+ -
+ $ref: '#/components/schemas/AutoArtifactSpec'
+ example:
+ auto: true
+ mountpath: /usr/src/app/persistent
+ name: my-files
+ size: 5Gi
+ usenfs: true
+ HarnessMainConfig:
+ description: ''
+ required:
+ - local
+ - secured_gatekeepers
+ - domain
+ - namespace
+ - mainapp
+ - apps
+ type: object
+ properties:
+ local:
+ description: 'If set to true, local DNS mapping is added to pods.'
+ type: boolean
+ secured_gatekeepers:
+ description: >-
+ Enables/disables Gatekeepers on secured applications. Set to false for
+ testing/development
+ type: boolean
+ domain:
+ description: The root domain
+ type: string
+ example: The root domain.
+ namespace:
+ description: The K8s namespace.
+ type: string
+ mainapp:
+ description: Defines the app to map to the root domain
+ type: string
+ registry:
+ $ref: '#/components/schemas/RegistryConfig'
+ description: ''
+ tag:
+ description: Docker tag used to push/pull the built images.
+ type: string
+ apps:
+ $ref: '#/components/schemas/ApplicationsConfigsMap'
+ description: ''
+ env:
+ description: Environmental variables added to all pods
+ type: array
+ items:
+ $ref: '#/components/schemas/NameValue'
+ privenv:
+ $ref: '#/components/schemas/NameValue'
+ description: Private environmental variables added to all pods
+ backup:
+ $ref: '#/components/schemas/BackupConfig'
+ description: ''
+ name:
+ description: Base name
+ type: string
+ task-images:
+ $ref: '#/components/schemas/SimpleMap'
+ description: ''
+ additionalProperties: true
+ SimpleMap:
+ description: ''
+ type: object
+ additionalProperties: true
+ Quota:
+ description: ''
+ type: object
+ additionalProperties: true
+ example:
+ quota-ws-max: 5
+ quota-storage-max: 1G
diff --git a/libraries/models/cloudharness_model/models/application_harness_config.py b/libraries/models/cloudharness_model/models/application_harness_config.py
index 4a083624..ba287654 100644
--- a/libraries/models/cloudharness_model/models/application_harness_config.py
+++ b/libraries/models/cloudharness_model/models/application_harness_config.py
@@ -37,7 +37,7 @@ class ApplicationHarnessConfig(Model):
Do not edit the class manually.
"""
- def __init__(self, deployment=None, service=None, subdomain=None, aliases=None, domain=None, dependencies=None, secured=None, uri_role_mapping=None, secrets=None, use_services=None, database=None, resources=None, readiness_probe=None, startup_probe=None, liveness_probe=None, source_root=None, name=None, jupyterhub=None, accounts=None, test=None): # noqa: E501
+ def __init__(self, deployment=None, service=None, subdomain=None, aliases=None, domain=None, dependencies=None, secured=None, uri_role_mapping=None, secrets=None, use_services=None, database=None, resources=None, readiness_probe=None, startup_probe=None, liveness_probe=None, source_root=None, name=None, jupyterhub=None, accounts=None, test=None, quotas=None): # noqa: E501
"""ApplicationHarnessConfig - a model defined in OpenAPI
:param deployment: The deployment of this ApplicationHarnessConfig. # noqa: E501
@@ -57,7 +57,7 @@ def __init__(self, deployment=None, service=None, subdomain=None, aliases=None,
:param uri_role_mapping: The uri_role_mapping of this ApplicationHarnessConfig. # noqa: E501
:type uri_role_mapping: List[UriRoleMappingConfig]
:param secrets: The secrets of this ApplicationHarnessConfig. # noqa: E501
- :type secrets: Dict[str, str]
+ :type secrets: Dict[str, object]
:param use_services: The use_services of this ApplicationHarnessConfig. # noqa: E501
:type use_services: List[str]
:param database: The database of this ApplicationHarnessConfig. # noqa: E501
@@ -80,6 +80,8 @@ def __init__(self, deployment=None, service=None, subdomain=None, aliases=None,
:type accounts: ApplicationAccountsConfig
:param test: The test of this ApplicationHarnessConfig. # noqa: E501
:type test: ApplicationTestConfig
+ :param quotas: The quotas of this ApplicationHarnessConfig. # noqa: E501
+ :type quotas: Dict[str, object]
"""
self.openapi_types = {
'deployment': DeploymentAutoArtifactConfig,
@@ -90,7 +92,7 @@ def __init__(self, deployment=None, service=None, subdomain=None, aliases=None,
'dependencies': ApplicationDependenciesConfig,
'secured': bool,
'uri_role_mapping': List[UriRoleMappingConfig],
- 'secrets': Dict[str, str],
+ 'secrets': Dict[str, object],
'use_services': List[str],
'database': DatabaseDeploymentConfig,
'resources': List[FileResourcesConfig],
@@ -101,7 +103,8 @@ def __init__(self, deployment=None, service=None, subdomain=None, aliases=None,
'name': str,
'jupyterhub': JupyterHubConfig,
'accounts': ApplicationAccountsConfig,
- 'test': ApplicationTestConfig
+ 'test': ApplicationTestConfig,
+ 'quotas': Dict[str, object]
}
self.attribute_map = {
@@ -124,7 +127,8 @@ def __init__(self, deployment=None, service=None, subdomain=None, aliases=None,
'name': 'name',
'jupyterhub': 'jupyterhub',
'accounts': 'accounts',
- 'test': 'test'
+ 'test': 'test',
+ 'quotas': 'quotas'
}
self._deployment = deployment
@@ -147,6 +151,7 @@ def __init__(self, deployment=None, service=None, subdomain=None, aliases=None,
self._jupyterhub = jupyterhub
self._accounts = accounts
self._test = test
+ self._quotas = quotas
@classmethod
def from_dict(cls, dikt) -> 'ApplicationHarnessConfig':
@@ -344,7 +349,7 @@ def secrets(self):
# noqa: E501
:return: The secrets of this ApplicationHarnessConfig.
- :rtype: Dict[str, str]
+ :rtype: Dict[str, object]
"""
return self._secrets
@@ -355,7 +360,7 @@ def secrets(self, secrets):
# noqa: E501
:param secrets: The secrets of this ApplicationHarnessConfig.
- :type secrets: Dict[str, str]
+ :type secrets: Dict[str, object]
"""
self._secrets = secrets
@@ -600,3 +605,26 @@ def test(self, test):
"""
self._test = test
+
+ @property
+ def quotas(self):
+ """Gets the quotas of this ApplicationHarnessConfig.
+
+ # noqa: E501
+
+ :return: The quotas of this ApplicationHarnessConfig.
+ :rtype: Dict[str, object]
+ """
+ return self._quotas
+
+ @quotas.setter
+ def quotas(self, quotas):
+ """Sets the quotas of this ApplicationHarnessConfig.
+
+ # noqa: E501
+
+ :param quotas: The quotas of this ApplicationHarnessConfig.
+ :type quotas: Dict[str, object]
+ """
+
+ self._quotas = quotas
diff --git a/libraries/models/cloudharness_model/models/harness_main_config.py b/libraries/models/cloudharness_model/models/harness_main_config.py
index d3b4af7f..c75db6d3 100644
--- a/libraries/models/cloudharness_model/models/harness_main_config.py
+++ b/libraries/models/cloudharness_model/models/harness_main_config.py
@@ -51,7 +51,7 @@ def __init__(self, local=None, secured_gatekeepers=None, domain=None, namespace=
:param name: The name of this HarnessMainConfig. # noqa: E501
:type name: str
:param task_images: The task_images of this HarnessMainConfig. # noqa: E501
- :type task_images: Dict[str, str]
+ :type task_images: Dict[str, object]
"""
self.openapi_types = {
'local': bool,
@@ -66,7 +66,7 @@ def __init__(self, local=None, secured_gatekeepers=None, domain=None, namespace=
'privenv': NameValue,
'backup': BackupConfig,
'name': str,
- 'task_images': Dict[str, str]
+ 'task_images': Dict[str, object]
}
self.attribute_map = {
@@ -399,7 +399,7 @@ def task_images(self):
# noqa: E501
:return: The task_images of this HarnessMainConfig.
- :rtype: Dict[str, str]
+ :rtype: Dict[str, object]
"""
return self._task_images
@@ -410,7 +410,7 @@ def task_images(self, task_images):
# noqa: E501
:param task_images: The task_images of this HarnessMainConfig.
- :type task_images: Dict[str, str]
+ :type task_images: Dict[str, object]
"""
self._task_images = task_images
diff --git a/libraries/models/cloudharness_model/models/jupyter_hub_config.py b/libraries/models/cloudharness_model/models/jupyter_hub_config.py
index 51316ff2..1f04fa73 100644
--- a/libraries/models/cloudharness_model/models/jupyter_hub_config.py
+++ b/libraries/models/cloudharness_model/models/jupyter_hub_config.py
@@ -21,7 +21,7 @@ def __init__(self, args=None, extra_config=None, spawner_extra_config=None, appl
:param args: The args of this JupyterHubConfig. # noqa: E501
:type args: List[str]
:param extra_config: The extra_config of this JupyterHubConfig. # noqa: E501
- :type extra_config: Dict[str, str]
+ :type extra_config: Dict[str, object]
:param spawner_extra_config: The spawner_extra_config of this JupyterHubConfig. # noqa: E501
:type spawner_extra_config: Dict[str, object]
:param application_hook: The application_hook of this JupyterHubConfig. # noqa: E501
@@ -29,7 +29,7 @@ def __init__(self, args=None, extra_config=None, spawner_extra_config=None, appl
"""
self.openapi_types = {
'args': List[str],
- 'extra_config': Dict[str, str],
+ 'extra_config': Dict[str, object],
'spawner_extra_config': Dict[str, object],
'application_hook': object
}
@@ -87,7 +87,7 @@ def extra_config(self):
# noqa: E501
:return: The extra_config of this JupyterHubConfig.
- :rtype: Dict[str, str]
+ :rtype: Dict[str, object]
"""
return self._extra_config
@@ -98,7 +98,7 @@ def extra_config(self, extra_config):
# noqa: E501
:param extra_config: The extra_config of this JupyterHubConfig.
- :type extra_config: Dict[str, str]
+ :type extra_config: Dict[str, object]
"""
self._extra_config = extra_config
diff --git a/libraries/models/cloudharness_model/models/user_group.py b/libraries/models/cloudharness_model/models/user_group.py
index 50d3b30d..72e135ef 100644
--- a/libraries/models/cloudharness_model/models/user_group.py
+++ b/libraries/models/cloudharness_model/models/user_group.py
@@ -21,7 +21,7 @@ def __init__(self, access=None, attributes=None, client_roles=None, id=None, nam
:param access: The access of this UserGroup. # noqa: E501
:type access: Dict[str, object]
:param attributes: The attributes of this UserGroup. # noqa: E501
- :type attributes: Dict[str, str]
+ :type attributes: Dict[str, object]
:param client_roles: The client_roles of this UserGroup. # noqa: E501
:type client_roles: Dict[str, object]
:param id: The id of this UserGroup. # noqa: E501
@@ -37,7 +37,7 @@ def __init__(self, access=None, attributes=None, client_roles=None, id=None, nam
"""
self.openapi_types = {
'access': Dict[str, object],
- 'attributes': Dict[str, str],
+ 'attributes': Dict[str, object],
'client_roles': Dict[str, object],
'id': str,
'name': str,
@@ -105,7 +105,7 @@ def attributes(self):
# noqa: E501
:return: The attributes of this UserGroup.
- :rtype: Dict[str, str]
+ :rtype: Dict[str, object]
"""
return self._attributes
@@ -116,7 +116,7 @@ def attributes(self, attributes):
# noqa: E501
:param attributes: The attributes of this UserGroup.
- :type attributes: Dict[str, str]
+ :type attributes: Dict[str, object]
"""
self._attributes = attributes
diff --git a/tools/cloudharness-test/cloudharness_test/api.py b/tools/cloudharness-test/cloudharness_test/api.py
index d7cc87bd..5193902d 100644
--- a/tools/cloudharness-test/cloudharness_test/api.py
+++ b/tools/cloudharness-test/cloudharness_test/api.py
@@ -60,6 +60,13 @@ def run_api_tests(root_paths, helm_values: HarnessMainConfig, base_domain, inclu
app_env = get_app_environment(app_config, app_domain)
+ schema_file = f"applications/{app_config.name}/api/openapi.yaml"
+
+ for path in root_paths:
+ # use local schema if available to simplify test development
+ if os.path.exists(os.path.join(path, schema_file)):
+ app_env["APP_SCHEMA_FILE"] = schema_file
+
if api_config.autotest:
logging.info("Running auto api tests")
cmd = get_schemathesis_command(api_filename, app_config, app_domain)
diff --git a/tools/cloudharness-test/cloudharness_test/apitest_init.py b/tools/cloudharness-test/cloudharness_test/apitest_init.py
index 4ba65f7e..1f83eabd 100644
--- a/tools/cloudharness-test/cloudharness_test/apitest_init.py
+++ b/tools/cloudharness-test/cloudharness_test/apitest_init.py
@@ -1,44 +1,64 @@
import os
import logging
+import requests
import schemathesis as st
from cloudharness.auth import get_token
-if "APP_URL" in os.environ:
+if "APP_URL" or "APP_SCHEMA_FILE" in os.environ:
+ app_schema = os.environ.get("APP_SCHEMA_FILE", None)
+ app_url = os.environ.get("APP_URL", "http://samples.ch.local/api")
+ logging.info("Start schemathesis tests on %s", app_url)
+ if app_schema:
+ # Test locally with harness-test -- use local schema for convenience during test development
+ openapi_uri = app_schema
+ schema = st.from_file(openapi_uri)
+ else:
+ # remote testing: might be /api/openapi.json or /openapi.json
+ try:
+ openapi_uri = openapi_uri = app_url + "/openapi.json"
+ schema = st.from_uri(openapi_uri)
+ except st.exceptions.SchemaLoadingError as e:
+ # Use alternative configuration
+ try:
+ openapi_uri = app_url.replace("/api", "") + "/openapi.json"
+ print(requests.get(openapi_uri))
+ schema = st.from_uri(openapi_uri)
+ except st.exceptions.SchemaLoadingError as e:
+ raise Exception(
+ f"Cannot setup api tests: {openapi_uri} not valid. Check your deployment is up and configuration") from e
- app_url = os.environ["APP_URL"]
+ except Exception as e:
+ raise Exception(
+ f"Cannot setup api tests: {openapi_uri}: {e}") from e
- try:
- openapi_uri = app_url + "/openapi.json"
- logging.info("Using openapi spec at %s", openapi_uri)
- schema = st.from_uri(openapi_uri)
- except Exception as e:
- raise Exception(f"Cannot setup api tests: {openapi_uri} not reachable. Check your deployment is up and configuration") from e
+ logging.info("Using openapi spec at %s", openapi_uri)
if "USERNAME" in os.environ and "PASSWORD" in os.environ:
logging.info("Setting token from username and password")
+
@st.auth.register()
class TokenAuth:
def get(self, context):
-
+
username = os.environ["USERNAME"]
- password = os.environ["PASSWORD"]
-
+ password = os.environ["PASSWORD"]
+
return get_token(username, password)
def set(self, case, data, context):
case.headers = case.headers or {}
- case.headers["Authorization"] = f"Bearer {data}"
- case.headers["Cookie"] = f"kc-access={data}"
+ case.headers["Authorization"] = f"Bearer {data}"
+ case.headers["Cookie"] = f"kc-access={data}"
else:
@st.auth.register()
class TokenAuth:
def get(self, context):
-
+
return ""
def set(self, case, data, context):
case.headers = case.headers or {}
- case.headers["Authorization"] = f"Bearer {data}"
- case.headers["Cookie"] = f"kc-access={data}"
\ No newline at end of file
+ case.headers["Authorization"] = f"Bearer {data}"
+ case.headers["Cookie"] = f"kc-access={data}"
diff --git a/tools/deployment-cli-tools/ch_cli_tools/skaffold.py b/tools/deployment-cli-tools/ch_cli_tools/skaffold.py
index 1e6742a5..774234b9 100644
--- a/tools/deployment-cli-tools/ch_cli_tools/skaffold.py
+++ b/tools/deployment-cli-tools/ch_cli_tools/skaffold.py
@@ -3,7 +3,7 @@
import json
import time
-from os.path import join, dirname, relpath, basename
+from os.path import join, relpath, basename, exists, abspath
from cloudharness_model import ApplicationTestConfig, HarnessMainConfig
from cloudharness_utils.constants import APPS_PATH, DEPLOYMENT_CONFIGURATION_PATH, \
@@ -51,7 +51,7 @@ def build_artifact(image_name, context_path, requirements=None, dockerfile_path=
in requirements]
return artifact_spec
-
+
base_images = set()
def process_build_dockerfile(dockerfile_path, root_path, global_context=False, requirements=None, app_name=None):
@@ -78,13 +78,13 @@ def process_build_dockerfile(dockerfile_path, root_path, global_context=False, r
for dockerfile_path in base_dockerfiles:
process_build_dockerfile(dockerfile_path, root_path, global_context=True)
-
-
+
+
release_config = skaffold_conf['deploy']['helm']['releases'][0]
release_config['name'] = helm_values.namespace
release_config['namespace'] = helm_values.namespace
release_config['artifactOverrides'][KEY_APPS] = {}
-
+
static_images = set()
for root_path in root_paths:
static_dockerfiles = find_dockerfiles_paths(
@@ -92,7 +92,7 @@ def process_build_dockerfile(dockerfile_path, root_path, global_context=False, r
for dockerfile_path in static_dockerfiles:
process_build_dockerfile(dockerfile_path, root_path)
-
+
for root_path in root_paths:
apps_path = join(root_path, APPS_PATH)
@@ -138,22 +138,45 @@ def process_build_dockerfile(dockerfile_path, root_path, global_context=False, r
}
}
- flask_main = find_file_paths(context_path, '__main__.py')
-
- if flask_main:
+ mains_candidates = find_file_paths(context_path, '__main__.py')
+
+ def identify_unicorn_based_main(candidates):
+ import re
+ gunicorn_pattern = re.compile(r"gunicorn")
+ for candidate in candidates:
+ dockerfile_path = f"{candidate}/.."
+ while not exists(f"{dockerfile_path}/Dockerfile") and abspath(dockerfile_path) != abspath(root_path):
+ dockerfile_path += "/.."
+ dockerfile = f"{dockerfile_path}/Dockerfile"
+ if not exists(dockerfile):
+ continue
+ with open(dockerfile, 'r') as file:
+ if re.search(gunicorn_pattern, file.read()):
+ return candidate
+ requirements = f"{candidate}/../requirements.txt"
+ if not exists(requirements):
+ continue
+ with open(requirements, 'r') as file:
+ if re.search(gunicorn_pattern, file.read()):
+ return candidate
+ return None
+
+ task_main_file = identify_unicorn_based_main(mains_candidates)
+
+ if task_main_file:
release_config['overrides']['apps'][app_key] = \
{
'harness': {
'deployment': {
'command': ['python'],
- 'args': [f'/usr/src/app/{os.path.basename(flask_main[0])}/__main__.py']
+ 'args': [f'/usr/src/app/{os.path.basename(task_main_file)}/__main__.py']
}
}
}
-
+
test_config: ApplicationTestConfig = helm_values.apps[app_key].harness.test
if test_config.unit.enabled and test_config.unit.commands:
-
+
skaffold_conf['test'].append(dict(
image=get_image_tag(app_name),
custom=[dict(command="docker run $IMAGE " + cmd) for cmd in test_config.unit.commands]
@@ -209,7 +232,7 @@ def get_image_tag(name):
"/usr/src/app"),
}
})
-
+
if not os.path.exists(os.path.dirname(vscode_launch_path)):
os.makedirs(os.path.dirname(vscode_launch_path))
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/.dockerignore b/tools/deployment-cli-tools/tests/resources_buggy/.dockerignore
new file mode 100644
index 00000000..bba89d5b
--- /dev/null
+++ b/tools/deployment-cli-tools/tests/resources_buggy/.dockerignore
@@ -0,0 +1,22 @@
+**/node_modules
+.tox
+docs
+applications
+/infrastructure
+/blueprint
+test
+/tools/deployment-cli-tools
+.github
+.git
+.vscode
+/deployment
+skaffold.yaml
+*.egg-info
+__pycache__
+.hypothesis
+.coverage
+.pytest_cache
+/application-templates
+/deployment-configuration
+/cloud-harness
+.openapi-generator
\ No newline at end of file
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/events/deploy/values-prod.yaml b/tools/deployment-cli-tools/tests/resources_buggy/applications/events/deploy/values-prod.yaml
new file mode 100644
index 00000000..d4d54b1f
--- /dev/null
+++ b/tools/deployment-cli-tools/tests/resources_buggy/applications/events/deploy/values-prod.yaml
@@ -0,0 +1,7 @@
+kafka:
+ resources:
+ limits:
+ cpu: overridden-prod
+ requests:
+ cpu: 50m
+ memory: 100Mi
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/events/deploy/values.yaml b/tools/deployment-cli-tools/tests/resources_buggy/applications/events/deploy/values.yaml
new file mode 100644
index 00000000..300997d5
--- /dev/null
+++ b/tools/deployment-cli-tools/tests/resources_buggy/applications/events/deploy/values.yaml
@@ -0,0 +1,8 @@
+kafka:
+ resources:
+ limits:
+ cpu: 600m
+ memory: overridden
+ requests:
+ cpu: 50m
+ memory: 100Mi
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/.dockerignore b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/.dockerignore
new file mode 100644
index 00000000..669be813
--- /dev/null
+++ b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/.dockerignore
@@ -0,0 +1 @@
+*.ignored
\ No newline at end of file
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/Dockerfile b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/Dockerfile
new file mode 100644
index 00000000..533a9c95
--- /dev/null
+++ b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/Dockerfile
@@ -0,0 +1,4 @@
+ARG CLOUDHARNESS_FLASK
+FROM $CLOUDHARNESS_FLASK
+
+RUN pip install gunicorn
\ No newline at end of file
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/deploy/resources/aresource.txt b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/deploy/resources/aresource.txt
new file mode 100644
index 00000000..e69de29b
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/deploy/templates/mytemplate.yaml b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/deploy/templates/mytemplate.yaml
new file mode 100644
index 00000000..e69de29b
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/deploy/values-dev.yaml b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/deploy/values-dev.yaml
new file mode 100644
index 00000000..0b7c9a77
--- /dev/null
+++ b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/deploy/values-dev.yaml
@@ -0,0 +1,11 @@
+harness:
+ name: "I'm useless"
+ subdomain: mysubdomain
+ dependencies:
+ soft:
+ - legacy
+ build:
+ - cloudharness-flask
+ - my-common
+a: b
+dev: true
\ No newline at end of file
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/deploy/values-test.yaml b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/deploy/values-test.yaml
new file mode 100644
index 00000000..85ecbfae
--- /dev/null
+++ b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/deploy/values-test.yaml
@@ -0,0 +1,2 @@
+a: test
+test: true
\ No newline at end of file
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/deploy/values-withmongo.yaml b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/deploy/values-withmongo.yaml
new file mode 100644
index 00000000..f3519b50
--- /dev/null
+++ b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/deploy/values-withmongo.yaml
@@ -0,0 +1,4 @@
+harness:
+ database:
+ auto: true
+ type: mongo
\ No newline at end of file
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/deploy/values-withoutdb.yaml b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/deploy/values-withoutdb.yaml
new file mode 100644
index 00000000..e69de29b
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/deploy/values-withpostgres.yaml b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/deploy/values-withpostgres.yaml
new file mode 100644
index 00000000..4b260dbf
--- /dev/null
+++ b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/deploy/values-withpostgres.yaml
@@ -0,0 +1,2 @@
+harness:
+ database: {auto: true, type: postgres}
\ No newline at end of file
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/deploy/values.yaml b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/deploy/values.yaml
new file mode 100644
index 00000000..434b042b
--- /dev/null
+++ b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/deploy/values.yaml
@@ -0,0 +1,14 @@
+harness:
+ name: "I'm useless"
+ subdomain: mysubdomain
+ dependencies:
+ soft:
+ - legacy
+ build:
+ - cloudharness-flask
+ test:
+ unit:
+ commands:
+ - tox
+ - echo "hello"
+a: b
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/myapp_code/__main__.py b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/myapp_code/__main__.py
new file mode 100644
index 00000000..3118318a
--- /dev/null
+++ b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/myapp_code/__main__.py
@@ -0,0 +1,9 @@
+#!/usr/bin/env python3
+
+from cloudharness.utils.server import init_flask, main
+
+
+app = init_flask(title="Cloudharness sample application", webapp=True)
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/tasks/mytask/Dockerfile b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/tasks/mytask/Dockerfile
new file mode 100644
index 00000000..e69de29b
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/venv/matplotlib/__main__.py b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/venv/matplotlib/__main__.py
new file mode 100644
index 00000000..37de2b0b
--- /dev/null
+++ b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp/venv/matplotlib/__main__.py
@@ -0,0 +1,7 @@
+#!/usr/bin/env python3
+
+from something import something_else
+
+
+def fake_content():
+ ...
\ No newline at end of file
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/.dockerignore b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/.dockerignore
new file mode 100644
index 00000000..669be813
--- /dev/null
+++ b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/.dockerignore
@@ -0,0 +1 @@
+*.ignored
\ No newline at end of file
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/Dockerfile b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/Dockerfile
new file mode 100644
index 00000000..de89f0ed
--- /dev/null
+++ b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/Dockerfile
@@ -0,0 +1,4 @@
+ARG CLOUDHARNESS_FLASK
+FROM $CLOUDHARNESS_FLASK
+
+RUN pip install -r requirements.txt
\ No newline at end of file
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/deploy/resources/aresource.txt b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/deploy/resources/aresource.txt
new file mode 100644
index 00000000..e69de29b
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/deploy/templates/mytemplate.yaml b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/deploy/templates/mytemplate.yaml
new file mode 100644
index 00000000..e69de29b
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/deploy/values-dev.yaml b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/deploy/values-dev.yaml
new file mode 100644
index 00000000..0b7c9a77
--- /dev/null
+++ b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/deploy/values-dev.yaml
@@ -0,0 +1,11 @@
+harness:
+ name: "I'm useless"
+ subdomain: mysubdomain
+ dependencies:
+ soft:
+ - legacy
+ build:
+ - cloudharness-flask
+ - my-common
+a: b
+dev: true
\ No newline at end of file
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/deploy/values-test.yaml b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/deploy/values-test.yaml
new file mode 100644
index 00000000..85ecbfae
--- /dev/null
+++ b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/deploy/values-test.yaml
@@ -0,0 +1,2 @@
+a: test
+test: true
\ No newline at end of file
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/deploy/values-withmongo.yaml b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/deploy/values-withmongo.yaml
new file mode 100644
index 00000000..f3519b50
--- /dev/null
+++ b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/deploy/values-withmongo.yaml
@@ -0,0 +1,4 @@
+harness:
+ database:
+ auto: true
+ type: mongo
\ No newline at end of file
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/deploy/values-withoutdb.yaml b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/deploy/values-withoutdb.yaml
new file mode 100644
index 00000000..e69de29b
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/deploy/values-withpostgres.yaml b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/deploy/values-withpostgres.yaml
new file mode 100644
index 00000000..4b260dbf
--- /dev/null
+++ b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/deploy/values-withpostgres.yaml
@@ -0,0 +1,2 @@
+harness:
+ database: {auto: true, type: postgres}
\ No newline at end of file
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/deploy/values.yaml b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/deploy/values.yaml
new file mode 100644
index 00000000..434b042b
--- /dev/null
+++ b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/deploy/values.yaml
@@ -0,0 +1,14 @@
+harness:
+ name: "I'm useless"
+ subdomain: mysubdomain
+ dependencies:
+ soft:
+ - legacy
+ build:
+ - cloudharness-flask
+ test:
+ unit:
+ commands:
+ - tox
+ - echo "hello"
+a: b
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/myapp_code/__main__.py b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/myapp_code/__main__.py
new file mode 100644
index 00000000..3118318a
--- /dev/null
+++ b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/myapp_code/__main__.py
@@ -0,0 +1,9 @@
+#!/usr/bin/env python3
+
+from cloudharness.utils.server import init_flask, main
+
+
+app = init_flask(title="Cloudharness sample application", webapp=True)
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/requirements.txt b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/requirements.txt
new file mode 100644
index 00000000..283e1305
--- /dev/null
+++ b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/requirements.txt
@@ -0,0 +1 @@
+gunicorn
\ No newline at end of file
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/tasks/mytask/Dockerfile b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/tasks/mytask/Dockerfile
new file mode 100644
index 00000000..e69de29b
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/venv/matplotlib/__main__.py b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/venv/matplotlib/__main__.py
new file mode 100644
index 00000000..37de2b0b
--- /dev/null
+++ b/tools/deployment-cli-tools/tests/resources_buggy/applications/myapp2/venv/matplotlib/__main__.py
@@ -0,0 +1,7 @@
+#!/usr/bin/env python3
+
+from something import something_else
+
+
+def fake_content():
+ ...
\ No newline at end of file
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/infrastructure/base-images/cloudharness-base/testfile b/tools/deployment-cli-tools/tests/resources_buggy/infrastructure/base-images/cloudharness-base/testfile
new file mode 100644
index 00000000..e69de29b
diff --git a/tools/deployment-cli-tools/tests/resources_buggy/infrastructure/common-images/my-common/Dockerfile b/tools/deployment-cli-tools/tests/resources_buggy/infrastructure/common-images/my-common/Dockerfile
new file mode 100644
index 00000000..e69de29b
diff --git a/tools/deployment-cli-tools/tests/test_skaffold.py b/tools/deployment-cli-tools/tests/test_skaffold.py
index 98036b21..94998892 100644
--- a/tools/deployment-cli-tools/tests/test_skaffold.py
+++ b/tools/deployment-cli-tools/tests/test_skaffold.py
@@ -7,6 +7,7 @@
HERE = os.path.dirname(os.path.realpath(__file__))
RESOURCES = os.path.join(HERE, 'resources')
+RESOURCES_BUGGY = os.path.join(HERE, 'resources_buggy')
OUT = '/tmp/deployment'
CLOUDHARNESS_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(HERE)))
@@ -70,7 +71,7 @@ def test_create_skaffold_configuration():
a for a in sk['build']['artifacts'] if a['image'] == 'reg/cloudharness/cloudharness-flask')
- assert os.path.samefile(cloudharness_flask_artifact['context'],
+ assert os.path.samefile(cloudharness_flask_artifact['context'],
join(CLOUDHARNESS_ROOT, 'infrastructure/common-images/cloudharness-flask')
)
@@ -102,3 +103,75 @@ def test_create_skaffold_configuration():
shutil.rmtree(OUT)
shutil.rmtree(BUILD_DIR)
+
+
+def test_create_skaffold_configuration_with_conflicting_dependencies(tmp_path):
+ values = create_helm_chart(
+ [CLOUDHARNESS_ROOT, RESOURCES_BUGGY],
+ output_path=OUT,
+ include=['myapp'],
+ exclude=['events'],
+ domain="my.local",
+ namespace='test',
+ env='dev',
+ local=False,
+ tag=1,
+ registry='reg'
+ )
+ root_paths = preprocess_build_overrides(
+ root_paths=[CLOUDHARNESS_ROOT, RESOURCES_BUGGY],
+ helm_values=values,
+ merge_build_path=str(tmp_path)
+ )
+
+ sk = create_skaffold_configuration(
+ root_paths=root_paths,
+ helm_values=values,
+ output_path=OUT
+ )
+
+ releases = sk['deploy']['helm']['releases']
+ assert len(releases) == 1 # Ensure we only found 1 deployment (for myapp)
+
+ release = releases[0]
+ assert 'myapp' in release['overrides']['apps']
+ assert 'matplotlib' not in release['overrides']['apps']
+
+ myapp_config = release['overrides']['apps']['myapp']
+ assert myapp_config['harness']['deployment']['args'][0] == '/usr/src/app/myapp_code/__main__.py'
+
+
+def test_create_skaffold_configuration_with_conflicting_dependencies_requirements_file(tmp_path):
+ values = create_helm_chart(
+ [CLOUDHARNESS_ROOT, RESOURCES_BUGGY],
+ output_path=OUT,
+ include=['myapp2'],
+ exclude=['events'],
+ domain="my.local",
+ namespace='test',
+ env='dev',
+ local=False,
+ tag=1,
+ registry='reg'
+ )
+ root_paths = preprocess_build_overrides(
+ root_paths=[CLOUDHARNESS_ROOT, RESOURCES_BUGGY],
+ helm_values=values,
+ merge_build_path=str(tmp_path)
+ )
+
+ sk = create_skaffold_configuration(
+ root_paths=root_paths,
+ helm_values=values,
+ output_path=OUT
+ )
+
+ releases = sk['deploy']['helm']['releases']
+ assert len(releases) == 1 # Ensure we only found 1 deployment (for myapp)
+
+ release = releases[0]
+ assert 'myapp2' in release['overrides']['apps']
+ assert 'matplotlib' not in release['overrides']['apps']
+
+ myapp_config = release['overrides']['apps']['myapp2']
+ assert myapp_config['harness']['deployment']['args'][0] == '/usr/src/app/myapp_code/__main__.py'
\ No newline at end of file