Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion applications/samples/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ RUN yarn build
FROM $CLOUDHARNESS_FLASK
ENV MODULE_NAME=samples

ENV WORKERS=2
ENV WORKERS=1
ENV PORT=8080

COPY backend/requirements.txt /usr/src/app/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
hey: "this"
is: "an instance overridden"
yaml: "file"
15 changes: 15 additions & 0 deletions applications/samples/deploy/instances/instance1/values-test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
harness:
subdomain: samples1
deployment:
replicas: 1
statefulset: false
resources:
limits:
memory: "300Mi"
requests:
cpu: "1m"
memory: "16Mi"
database:
connect_string: "instance1 connection string"
envmap:
ENVIRONMENT_TEST_A: "instance1 value"
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,8 @@ Usage: {{ include "deploy_utils.secretManagerResources" (dict "root" $root "app"
{{- if include "deploy_utils.secretIsExternal" (dict "spec" $spec) }}
{{- $manager := include "deploy_utils.secretManager" (dict "spec" $spec) }}
{{- $context := dict "root" $root "app" $app "name" $name "spec" $spec "resourceName" (include "deploy_utils.secretResourceName" (dict "app" $app "name" $name)) }}
---
{{ include (printf "deploy_utils.secretmanager.%s.resource" $manager) $context }}
---
{{- end }}
{{- end }}
{{- end -}}
44 changes: 23 additions & 21 deletions deployment/codefresh-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ steps:
buildkit: true
build_arguments:
- NOCACHE=${{CF_BUILD_ID}}
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- GOLANG=golang:1.26
- ROCKYLINUX=rockylinux/rockylinux:10.1-minimal
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- NODE=node:22-alpine
- PYTHON=python:3.12-slim-trixie
image_name: cloud-harness/accounts
Expand All @@ -82,9 +82,9 @@ steps:
buildkit: true
build_arguments:
- NOCACHE=${{CF_BUILD_ID}}
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- GOLANG=golang:1.26
- ROCKYLINUX=rockylinux/rockylinux:10.1-minimal
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- NODE=node:22-alpine
- PYTHON=python:3.12-slim-trixie
image_name: cloud-harness/cloudharness-base
Expand All @@ -109,9 +109,9 @@ steps:
buildkit: true
build_arguments:
- NOCACHE=${{CF_BUILD_ID}}
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- GOLANG=golang:1.26
- ROCKYLINUX=rockylinux/rockylinux:10.1-minimal
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- NODE=node:22-alpine
- PYTHON=python:3.12-slim-trixie
image_name: cloud-harness/cloudharness-frontend-build
Expand All @@ -136,9 +136,9 @@ steps:
buildkit: true
build_arguments:
- NOCACHE=${{CF_BUILD_ID}}
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- GOLANG=golang:1.26
- ROCKYLINUX=rockylinux/rockylinux:10.1-minimal
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- NODE=node:22-alpine
- PYTHON=python:3.12-slim-trixie
image_name: cloud-harness/nfsserver
Expand All @@ -163,9 +163,9 @@ steps:
buildkit: true
build_arguments:
- NOCACHE=${{CF_BUILD_ID}}
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- GOLANG=golang:1.26
- ROCKYLINUX=rockylinux/rockylinux:10.1-minimal
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- NODE=node:22-alpine
- PYTHON=python:3.12-slim-trixie
image_name: cloud-harness/test-e2e
Expand Down Expand Up @@ -197,9 +197,9 @@ steps:
build_arguments:
- NOCACHE=${{CF_BUILD_ID}}
- CLOUDHARNESS_BASE=${{REGISTRY}}/cloud-harness/cloudharness-base:${{CLOUDHARNESS_BASE_TAG}}
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- GOLANG=golang:1.26
- ROCKYLINUX=rockylinux/rockylinux:10.1-minimal
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- NODE=node:22-alpine
- PYTHON=python:3.12-slim-trixie
image_name: cloud-harness/cloudharness-django
Expand All @@ -225,9 +225,9 @@ steps:
build_arguments:
- NOCACHE=${{CF_BUILD_ID}}
- CLOUDHARNESS_BASE=${{REGISTRY}}/cloud-harness/cloudharness-base:${{CLOUDHARNESS_BASE_TAG}}
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- GOLANG=golang:1.26
- ROCKYLINUX=rockylinux/rockylinux:10.1-minimal
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- NODE=node:22-alpine
- PYTHON=python:3.12-slim-trixie
image_name: cloud-harness/cloudharness-flask
Expand All @@ -253,9 +253,9 @@ steps:
build_arguments:
- NOCACHE=${{CF_BUILD_ID}}
- CLOUDHARNESS_BASE=${{REGISTRY}}/cloud-harness/cloudharness-base:${{CLOUDHARNESS_BASE_TAG}}
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- GOLANG=golang:1.26
- ROCKYLINUX=rockylinux/rockylinux:10.1-minimal
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- NODE=node:22-alpine
- PYTHON=python:3.12-slim-trixie
image_name: cloud-harness/jupyterhub
Expand All @@ -281,9 +281,9 @@ steps:
build_arguments:
- NOCACHE=${{CF_BUILD_ID}}
- CLOUDHARNESS_BASE=${{REGISTRY}}/cloud-harness/cloudharness-base:${{CLOUDHARNESS_BASE_TAG}}
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- GOLANG=golang:1.26
- ROCKYLINUX=rockylinux/rockylinux:10.1-minimal
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- NODE=node:22-alpine
- PYTHON=python:3.12-slim-trixie
image_name: cloud-harness/sampleapp-print-file
Expand All @@ -309,9 +309,9 @@ steps:
build_arguments:
- NOCACHE=${{CF_BUILD_ID}}
- CLOUDHARNESS_BASE=${{REGISTRY}}/cloud-harness/cloudharness-base:${{CLOUDHARNESS_BASE_TAG}}
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- GOLANG=golang:1.26
- ROCKYLINUX=rockylinux/rockylinux:10.1-minimal
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- NODE=node:22-alpine
- PYTHON=python:3.12-slim-trixie
image_name: cloud-harness/sampleapp-secret
Expand All @@ -337,9 +337,9 @@ steps:
build_arguments:
- NOCACHE=${{CF_BUILD_ID}}
- CLOUDHARNESS_BASE=${{REGISTRY}}/cloud-harness/cloudharness-base:${{CLOUDHARNESS_BASE_TAG}}
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- GOLANG=golang:1.26
- ROCKYLINUX=rockylinux/rockylinux:10.1-minimal
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- NODE=node:22-alpine
- PYTHON=python:3.12-slim-trixie
image_name: cloud-harness/test-api
Expand All @@ -366,9 +366,9 @@ steps:
build_arguments:
- NOCACHE=${{CF_BUILD_ID}}
- CLOUDHARNESS_BASE=${{REGISTRY}}/cloud-harness/cloudharness-base:${{CLOUDHARNESS_BASE_TAG}}
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- GOLANG=golang:1.26
- ROCKYLINUX=rockylinux/rockylinux:10.1-minimal
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- NODE=node:22-alpine
- PYTHON=python:3.12-slim-trixie
image_name: cloud-harness/workflows-extract-download
Expand All @@ -394,9 +394,9 @@ steps:
build_arguments:
- NOCACHE=${{CF_BUILD_ID}}
- CLOUDHARNESS_BASE=${{REGISTRY}}/cloud-harness/cloudharness-base:${{CLOUDHARNESS_BASE_TAG}}
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- GOLANG=golang:1.26
- ROCKYLINUX=rockylinux/rockylinux:10.1-minimal
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- NODE=node:22-alpine
- PYTHON=python:3.12-slim-trixie
image_name: cloud-harness/workflows-notify-queue
Expand All @@ -422,9 +422,9 @@ steps:
build_arguments:
- NOCACHE=${{CF_BUILD_ID}}
- CLOUDHARNESS_BASE=${{REGISTRY}}/cloud-harness/cloudharness-base:${{CLOUDHARNESS_BASE_TAG}}
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- GOLANG=golang:1.26
- ROCKYLINUX=rockylinux/rockylinux:10.1-minimal
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- NODE=node:22-alpine
- PYTHON=python:3.12-slim-trixie
image_name: cloud-harness/workflows-send-result-event
Expand Down Expand Up @@ -455,9 +455,9 @@ steps:
build_arguments:
- NOCACHE=${{CF_BUILD_ID}}
- CLOUDHARNESS_FLASK=${{REGISTRY}}/cloud-harness/cloudharness-flask:${{CLOUDHARNESS_FLASK_TAG}}
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- GOLANG=golang:1.26
- ROCKYLINUX=rockylinux/rockylinux:10.1-minimal
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- NODE=node:22-alpine
- PYTHON=python:3.12-slim-trixie
image_name: cloud-harness/common
Expand All @@ -484,9 +484,9 @@ steps:
- NOCACHE=${{CF_BUILD_ID}}
- CLOUDHARNESS_FRONTEND_BUILD=${{REGISTRY}}/cloud-harness/cloudharness-frontend-build:${{CLOUDHARNESS_FRONTEND_BUILD_TAG}}
- CLOUDHARNESS_FLASK=${{REGISTRY}}/cloud-harness/cloudharness-flask:${{CLOUDHARNESS_FLASK_TAG}}
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- GOLANG=golang:1.26
- ROCKYLINUX=rockylinux/rockylinux:10.1-minimal
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- NODE=node:22-alpine
- PYTHON=python:3.12-slim-trixie
image_name: cloud-harness/sampleapp
Expand All @@ -512,9 +512,9 @@ steps:
build_arguments:
- NOCACHE=${{CF_BUILD_ID}}
- CLOUDHARNESS_FLASK=${{REGISTRY}}/cloud-harness/cloudharness-flask:${{CLOUDHARNESS_FLASK_TAG}}
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- GOLANG=golang:1.26
- ROCKYLINUX=rockylinux/rockylinux:10.1-minimal
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- NODE=node:22-alpine
- PYTHON=python:3.12-slim-trixie
image_name: cloud-harness/volumemanager
Expand All @@ -540,9 +540,9 @@ steps:
build_arguments:
- NOCACHE=${{CF_BUILD_ID}}
- CLOUDHARNESS_FLASK=${{REGISTRY}}/cloud-harness/cloudharness-flask:${{CLOUDHARNESS_FLASK_TAG}}
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- GOLANG=golang:1.26
- ROCKYLINUX=rockylinux/rockylinux:10.1-minimal
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- NODE=node:22-alpine
- PYTHON=python:3.12-slim-trixie
image_name: cloud-harness/workflows
Expand Down Expand Up @@ -573,9 +573,9 @@ steps:
build_arguments:
- NOCACHE=${{CF_BUILD_ID}}
- SAMPLES=${{REGISTRY}}/cloud-harness/sampleapp:${{SAMPLES_TAG}}
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- GOLANG=golang:1.26
- ROCKYLINUX=rockylinux/rockylinux:10.1-minimal
- KEYCLOAK=quay.io/keycloak/keycloak:26.5
- NODE=node:22-alpine
- PYTHON=python:3.12-slim-trixie
image_name: cloud-harness/sampleapp-sum
Expand Down Expand Up @@ -618,6 +618,7 @@ steps:
custom_value_files:
- ./deployment/helm/values.yaml
custom_values:
- 'apps_samples-instance1_harness_secrets_asecret="${{ASECRET}}"'
- 'apps_samples_harness_secrets_asecret="${{ASECRET}}"'
wait_deployment:
stage: qa
Expand All @@ -627,10 +628,11 @@ steps:
commands:
- kubectl config use-context ${{CLUSTER_NAME}}
- kubectl config set-context --current --namespace=test-${{NAMESPACE_BASENAME}}
- kubectl rollout status statefulset/samples
- kubectl rollout status deployment/common
- kubectl rollout status deployment/volumemanager
- kubectl rollout status deployment/samples-instance1
- kubectl rollout status deployment/accounts
- kubectl rollout status statefulset/samples
- kubectl rollout status deployment/common
- kubectl rollout status deployment/workflows
- sleep 60
tests_nfs_failover:
Expand Down
87 changes: 86 additions & 1 deletion docs/applications/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,91 @@ harness:

To customize the helm templates to use, put them inside the *deploy* subdirectory.

## Multiple application instances

An application can be deployed several times over, each deployment on its own subdomain and with
its own configuration and database. Each of those deployments is an *instance*, declared as a
directory under the application's `deploy/instances`, such as:

```
applications/myapp/
Dockerfile
deploy/
values.yaml # the application's configuration
resources/
example.yaml
myConfig.json
instances/
instance1/ # the instance's name is its directory's
values.yaml # what this instance changes
resources/
example.yaml # overrides the application's resource of the same name
templates/ # optional, overlaid on the application's templates
```

The instance above is deployed as the application `myapp-instance1`: that key names its service,
deployment, database, volume, gatekeeper and configmaps, and is how it is referenced on the command
line. It runs the image built for `myapp` — an instance adds no build, so declare no Dockerfile
in it.

Everything else is inherited from the application, so an instance's `values.yaml` only carries what
it changes:

```yaml
harness:
subdomain: myapp1
deployment:
replicas: 1
```

Values are merged over the application's the usual way: mappings key by key, lists as a whole. An
instance overriding `uri_role_mapping` therefore replaces the whole list rather than adding to it.
Resources and templates are overlaid file by file, so an instance inherits every file it does not
override — above, `myConfig.json` comes from the application and `example.yaml` from the instance.

### Envs and values precedence resolution
Environment specific values apply at both levels, the instance's taking precedence over the
application's:

```
deploy/instances/instance1/values-[ENV].yaml # wins
deploy/instances/instance1/values.yaml
deploy/values-[ENV].yaml
deploy/values.yaml # loses
```

An instance is declared by its values files: with a `values.yaml` it is deployed in every
environment, with only a `values-[ENV].yaml` it is deployed in that environment alone (with
`harness-deployment -e ENV`). A directory with neither is ignored.

### Inheritance exceptions
What identifies the application is never inherited, so to avoid collisions across
application's hosts or resources:

- `subdomain`, `aliases` and `domain`. An instance without a `subdomain` of its own answers on
its directory's name, so `instances/myapp1/` with an empty `values.yaml` is served at
`myapp1.[DOMAIN]`; declare `subdomain: null` to give an instance no ingress at all
- the names of the service, deployment and database, which are derived from the instance key
- `deployment.volume.name` when the volume is automatic (`auto` unset or `true`): prefixed with
the instance name (`instance1-my-shared-volume`), so the instance gets a claim of its own instead
of mounting the application's storage. A non-automatic volume is a pre-existing claim and stays
shared
- `database.connect_string`, emptied: an instance of an application using an externally managed
database needs a connection string of its own. Set `database.auto: true` to have CloudHarness
deploy a database of its own for it instead.

### Database sharing
An instance may share the application's database server by explitly declaring its `database.name`
(For instance, `myapp-db` for `myapp`), and not overriding the name in the instance (or using the same).
When the database instance is shared, its initial database is then named after the instance application,
hyphens turned to underscores (`myapp_instance1`), so the two never share data. Declare
`postgres.initialdb` on the instance to pick the name yourself.

### Deployment
Instances are deployed together with their application: `harness-deployment -i myapp` deploys
`myapp` and all its instances, and `-ex myapp-instance1` leaves one out. CI builds and tests the
application only, since an instance runs the same image.

## Dependency to an existing Helm chart

TBD
Expand Down Expand Up @@ -128,6 +213,6 @@ The most important configuration entries are the following:
- `buildArgs`: a map of build arguments to provide to the dockerfile when building with Skaffold

# Example code
- [Sample application](../../applications/samples) is a sample web application providing working examples of deployment configuration, backend and frontend code.
- [Sample application](../../applications/myapp) is a sample web application providing working examples of deployment configuration, backend and frontend code.


2 changes: 1 addition & 1 deletion tools/cloudharness-test/cloudharness_test/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import logging
import subprocess

from ch_cli_tools.preprocessing import get_build_paths
from ch_cli_tools.configuration.preprocessing import get_build_paths


from cloudharness_model.models import HarnessMainConfig, ApiTestsConfig, ApplicationHarnessConfig
Expand Down
2 changes: 1 addition & 1 deletion tools/cloudharness-test/cloudharness_test/e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from cloudharness_model.models import ApplicationHarnessConfig

from cloudharness_utils.constants import E2E_TESTS_PROJECT_PATH, E2E_TESTS_DIRNAME
from ch_cli_tools.preprocessing import get_build_paths
from ch_cli_tools.configuration.preprocessing import get_build_paths
from cloudharness_utils.testing.util import get_app_environment

HERE = os.path.dirname(os.path.realpath(__file__)).replace(os.path.sep, '/')
Expand Down
4 changes: 2 additions & 2 deletions tools/deployment-cli-tools/ch_cli_tools/codefresh.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
from cloudharness_utils.testing.util import get_app_environment
from .models import HarnessMainConfig, ApplicationTestConfig, ApplicationHarnessConfig
from cloudharness_utils.constants import *
from .configurationgenerator import KEY_APPS, KEY_TASK_IMAGES, KEY_TEST_IMAGES
from .secrets import is_cloudharness_managed, is_secret_config, secret_value
from .constants import KEY_APPS, KEY_TASK_IMAGES, KEY_TEST_IMAGES
from .configuration.secrets import is_cloudharness_managed, is_secret_config, secret_value
from .utils import check_image_exists_in_registry, find_dockerfiles_paths, get_app_relative_to_base_path, guess_build_dependencies_from_dockerfile, \
get_template, dict_merge, app_name_from_path, clean_path, strip_registry_tag, get_image_source, yaml, yaml_rt
from cloudharness_utils.testing.api import get_api_filename, get_schemathesis_command, get_urls_from_api_file
Expand Down
Loading
Loading