From 8bd88b2be38c03760ec17df727db663768a2c0fd Mon Sep 17 00:00:00 2001 From: Ludal Date: Fri, 27 Feb 2026 09:03:50 +0100 Subject: [PATCH 01/33] CH-243: Make route pattern customizable --- applications/samples/deploy/values.yaml | 3 ++- deployment-configuration/helm/templates/ingress.yaml | 8 +++++--- deployment-configuration/helm/values.yaml | 4 +++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/applications/samples/deploy/values.yaml b/applications/samples/deploy/values.yaml index 607c9944..23886789 100644 --- a/applications/samples/deploy/values.yaml +++ b/applications/samples/deploy/values.yaml @@ -7,6 +7,7 @@ harness: service: port: 8080 auto: true + route_pattern: '(?!\/metrics\/?$)\/.*' use_services: - name: common deployment: @@ -96,4 +97,4 @@ harness: dockerfile: buildArgs: - TEST_ARGUMENT: example value \ No newline at end of file + TEST_ARGUMENT: example value diff --git a/deployment-configuration/helm/templates/ingress.yaml b/deployment-configuration/helm/templates/ingress.yaml index 5d3ea8ee..bfbb9466 100644 --- a/deployment-configuration/helm/templates/ingress.yaml +++ b/deployment-configuration/helm/templates/ingress.yaml @@ -2,6 +2,7 @@ {{ $domain := .root.Values.domain }} {{ $secured_gatekeepers := and .root.Values.secured_gatekeepers }} {{ $app := .app }} + {{- $routePattern := default .Values.ingress.route_pattern $app.harness.route_pattern }} http: paths: {{- if and $app.harness.secured $secured_gatekeepers $app.harness.uri_role_mapping }} @@ -39,7 +40,7 @@ {{- end }} {{- end }} {{- end }} - - path: /(.*) + - path: /{{ $routePattern }} pathType: ImplementationSpecific backend: service: @@ -52,7 +53,8 @@ {{ $domain := .root.Values.domain }} {{ $secured_gatekeepers := and .root.Values.secured_gatekeepers }} {{ $app := get .root.Values.apps .service_name}} - - path: /proxy/{{ $app.harness.service.name }}/(.*) + {{- $routePattern := default .Values.ingress.route_pattern $app.harness.route_pattern }} + - path: /proxy/{{ $app.harness.service.name }}/{{ $routePattern }} pathType: ImplementationSpecific backend: service: @@ -147,4 +149,4 @@ spec: {{- end }} secretName: tls-secret {{- end }} -{{- end }} \ No newline at end of file +{{- end }} diff --git a/deployment-configuration/helm/values.yaml b/deployment-configuration/helm/values.yaml index 8b83c047..07391f3a 100644 --- a/deployment-configuration/helm/values.yaml +++ b/deployment-configuration/helm/values.yaml @@ -40,6 +40,8 @@ ingress: letsencrypt: # -- Email for letsencrypt. email: cloudharness@metacell.us + # -- Default regex segment for routes (used in paths like '/(pattern)'). + route_pattern: "(.*)" backup: # -- Flag to enable/disable backups. active: false @@ -89,4 +91,4 @@ proxy: requests: memory: "32Mi" limits: - memory: "64Mi" \ No newline at end of file + memory: "64Mi" From 61961bbc39b64b24c08ec6f0c4c057f2b31a5c0b Mon Sep 17 00:00:00 2001 From: Ludal Date: Sat, 28 Feb 2026 10:21:36 +0100 Subject: [PATCH 02/33] Include forward slash in route pattern regex --- applications/samples/deploy/values.yaml | 2 +- deployment-configuration/helm/templates/ingress.yaml | 4 ++-- deployment-configuration/helm/values.yaml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/applications/samples/deploy/values.yaml b/applications/samples/deploy/values.yaml index 23886789..0d3b9648 100644 --- a/applications/samples/deploy/values.yaml +++ b/applications/samples/deploy/values.yaml @@ -7,7 +7,7 @@ harness: service: port: 8080 auto: true - route_pattern: '(?!\/metrics\/?$)\/.*' + route_pattern: '/(?!\/metrics\/?$)\/.*' use_services: - name: common deployment: diff --git a/deployment-configuration/helm/templates/ingress.yaml b/deployment-configuration/helm/templates/ingress.yaml index bfbb9466..f0f194c8 100644 --- a/deployment-configuration/helm/templates/ingress.yaml +++ b/deployment-configuration/helm/templates/ingress.yaml @@ -40,7 +40,7 @@ {{- end }} {{- end }} {{- end }} - - path: /{{ $routePattern }} + - path: {{ $routePattern }} pathType: ImplementationSpecific backend: service: @@ -54,7 +54,7 @@ {{ $secured_gatekeepers := and .root.Values.secured_gatekeepers }} {{ $app := get .root.Values.apps .service_name}} {{- $routePattern := default .Values.ingress.route_pattern $app.harness.route_pattern }} - - path: /proxy/{{ $app.harness.service.name }}/{{ $routePattern }} + - path: /proxy/{{ $app.harness.service.name }}{{ $routePattern }} pathType: ImplementationSpecific backend: service: diff --git a/deployment-configuration/helm/values.yaml b/deployment-configuration/helm/values.yaml index 07391f3a..8468b96a 100644 --- a/deployment-configuration/helm/values.yaml +++ b/deployment-configuration/helm/values.yaml @@ -41,7 +41,7 @@ ingress: # -- Email for letsencrypt. email: cloudharness@metacell.us # -- Default regex segment for routes (used in paths like '/(pattern)'). - route_pattern: "(.*)" + route_pattern: "/(.*)" backup: # -- Flag to enable/disable backups. active: false From 8dde96f9cc8be8d243f5d24d3a7e2d8f2bf6b5f0 Mon Sep 17 00:00:00 2001 From: Ludal Date: Sat, 28 Feb 2026 11:08:27 +0100 Subject: [PATCH 03/33] Fix indent --- applications/samples/deploy/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/samples/deploy/values.yaml b/applications/samples/deploy/values.yaml index 0d3b9648..55bcb0d4 100644 --- a/applications/samples/deploy/values.yaml +++ b/applications/samples/deploy/values.yaml @@ -7,7 +7,7 @@ harness: service: port: 8080 auto: true - route_pattern: '/(?!\/metrics\/?$)\/.*' + route_pattern: '/(?!\/metrics\/?$)\/.*' use_services: - name: common deployment: From 4238c752d84e21ea622e76ccb42ba78bdeb34c47 Mon Sep 17 00:00:00 2001 From: Ludal Date: Sat, 28 Feb 2026 11:24:44 +0100 Subject: [PATCH 04/33] Add documentation --- docs/ingress-domains-proxies.md | 36 ++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/docs/ingress-domains-proxies.md b/docs/ingress-domains-proxies.md index 08b86f5c..349ac53f 100644 --- a/docs/ingress-domains-proxies.md +++ b/docs/ingress-domains-proxies.md @@ -68,4 +68,38 @@ proxy: 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 +See also the [gatekeepers documentation](./accounts.md#secure-and-enpoint-with-the-gatekeeper). + +## Route pattern configuration + +Cloud Harness allows customizing which request paths are routed to an application +via a glob-style regular expression. There are two levels where this can be set: + +- **Global**: `ingress.route_pattern` in `deployment-configuration/helm/values.yaml` (applies to all apps by default). +- **Application**: `harness.route_pattern` in an application's `values.yaml` (overrides the global value for that app). + +The Helm ingress template uses the application-level `harness.route_pattern` when present, +falling back to the global `ingress.route_pattern` otherwise. + +Example (global default in `deployment-configuration/helm/values.yaml`): + +```yaml +ingress: + # Default regex segment for routes (used in paths like '/(pattern)') + route_pattern: "/(.*)" +``` + +Example (application override in `applications//deploy/values.yaml`): + +```yaml +harness: + # route_pattern is used to build the Ingress path for the app + route_pattern: '/((?!(?:metrics)(?:/)?$).*)' # exclude only '/metrics' and '/metrics/' +``` + +Notes: +- The pattern is inserted into the generated Ingress `path` field. Make sure the regex + is valid for your ingress controller and matches the expected path syntax. +- If you only need to exclude a single exact path (for example `/metrics`), use a + negative lookahead like the example above. If you prefer a simpler global default, + leave `ingress.route_pattern` as `"/(.*)"` and override per-app when needed. From 6a7d5661dfb29fe7e1535f0822af47c38d3917f5 Mon Sep 17 00:00:00 2001 From: Ludal Date: Sun, 1 Mar 2026 12:44:22 +0100 Subject: [PATCH 05/33] Fix linting --- tools/deployment-cli-tools/tests/test_codefresh.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/deployment-cli-tools/tests/test_codefresh.py b/tools/deployment-cli-tools/tests/test_codefresh.py index 5f096f33..65bd1fc2 100644 --- a/tools/deployment-cli-tools/tests/test_codefresh.py +++ b/tools/deployment-cli-tools/tests/test_codefresh.py @@ -483,7 +483,7 @@ def test_steps_ordered_by_stage_in_generated_config(): assert step_stage_indices == sorted(step_stage_indices), \ "Steps are not ordered by stage. Got stages: " + str( [step.get('stage') for step in steps.values() if step and isinstance(step, dict)] - ) + ) finally: import shutil shutil.rmtree(BUILD_MERGE_DIR, ignore_errors=True) From 3dc0062dc7b38621c2f4fc6b7334d28157f880fd Mon Sep 17 00:00:00 2001 From: Ludal Date: Sun, 1 Mar 2026 13:18:15 +0100 Subject: [PATCH 06/33] Try fix nil pointer --- deployment-configuration/helm/templates/ingress.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/deployment-configuration/helm/templates/ingress.yaml b/deployment-configuration/helm/templates/ingress.yaml index f0f194c8..baa3db5a 100644 --- a/deployment-configuration/helm/templates/ingress.yaml +++ b/deployment-configuration/helm/templates/ingress.yaml @@ -2,7 +2,6 @@ {{ $domain := .root.Values.domain }} {{ $secured_gatekeepers := and .root.Values.secured_gatekeepers }} {{ $app := .app }} - {{- $routePattern := default .Values.ingress.route_pattern $app.harness.route_pattern }} http: paths: {{- if and $app.harness.secured $secured_gatekeepers $app.harness.uri_role_mapping }} @@ -40,7 +39,7 @@ {{- end }} {{- end }} {{- end }} - - path: {{ $routePattern }} + - path: {{ default .Values.ingress.route_pattern $app.harness.route_pattern }} pathType: ImplementationSpecific backend: service: @@ -53,7 +52,7 @@ {{ $domain := .root.Values.domain }} {{ $secured_gatekeepers := and .root.Values.secured_gatekeepers }} {{ $app := get .root.Values.apps .service_name}} - {{- $routePattern := default .Values.ingress.route_pattern $app.harness.route_pattern }} + {{ $routePattern := default .Values.ingress.route_pattern $app.harness.route_pattern }} - path: /proxy/{{ $app.harness.service.name }}{{ $routePattern }} pathType: ImplementationSpecific backend: From af4a92e5ea22828b28e5fe9843482222a1613e49 Mon Sep 17 00:00:00 2001 From: Ludal Date: Sun, 1 Mar 2026 13:41:43 +0100 Subject: [PATCH 07/33] Fix nil pointer --- deployment-configuration/helm/templates/ingress.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deployment-configuration/helm/templates/ingress.yaml b/deployment-configuration/helm/templates/ingress.yaml index baa3db5a..4f7206ce 100644 --- a/deployment-configuration/helm/templates/ingress.yaml +++ b/deployment-configuration/helm/templates/ingress.yaml @@ -39,7 +39,7 @@ {{- end }} {{- end }} {{- end }} - - path: {{ default .Values.ingress.route_pattern $app.harness.route_pattern }} + - path: {{ default .root.Values.ingress.route_pattern $app.harness.route_pattern }} pathType: ImplementationSpecific backend: service: @@ -52,7 +52,7 @@ {{ $domain := .root.Values.domain }} {{ $secured_gatekeepers := and .root.Values.secured_gatekeepers }} {{ $app := get .root.Values.apps .service_name}} - {{ $routePattern := default .Values.ingress.route_pattern $app.harness.route_pattern }} + {{ $routePattern := default .root.Values.ingress.route_pattern $app.harness.route_pattern }} - path: /proxy/{{ $app.harness.service.name }}{{ $routePattern }} pathType: ImplementationSpecific backend: From 0ae1782f7884122f0682734cd526cc4ac62f966b Mon Sep 17 00:00:00 2001 From: Ludal Date: Sun, 1 Mar 2026 15:24:13 +0100 Subject: [PATCH 08/33] Add router path matcher Traefik annotation --- deployment-configuration/helm/templates/ingress.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/deployment-configuration/helm/templates/ingress.yaml b/deployment-configuration/helm/templates/ingress.yaml index 4f7206ce..ea001f7a 100644 --- a/deployment-configuration/helm/templates/ingress.yaml +++ b/deployment-configuration/helm/templates/ingress.yaml @@ -87,6 +87,7 @@ metadata: nginx.ingress.kubernetes.io/proxy-read-timeout: {{ .Values.proxy.timeout.read | quote }} nginx.ingress.kubernetes.io/proxy-send-timeout: {{ .Values.proxy.timeout.send | quote }} nginx.ingress.kubernetes.io/use-forwarded-headers: {{ .Values.proxy.forwardedHeaders | quote }} + traefik.ingress.kubernetes.io/router.pathmatcher: "PathRegexp" spec: ingressClassName: nginx rules: From 56439abde7a3464783dc75888780e5bfc746f786 Mon Sep 17 00:00:00 2001 From: Alex Burdusel Date: Fri, 6 Mar 2026 14:35:26 +0200 Subject: [PATCH 09/33] Add configurable gatekeeper secret key secret. If no secret key value is given, the secret is randomly generated. --- .../helm/templates/auto-gatekeepers.yaml | 5 +++-- deployment-configuration/value-template.yaml | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/deployment-configuration/helm/templates/auto-gatekeepers.yaml b/deployment-configuration/helm/templates/auto-gatekeepers.yaml index 2e1e6197..8a195f7e 100644 --- a/deployment-configuration/helm/templates/auto-gatekeepers.yaml +++ b/deployment-configuration/helm/templates/auto-gatekeepers.yaml @@ -27,6 +27,8 @@ data: forbidden-page: /templates/access-denied.html.tmpl enable-default-deny: {{ $noWildcards }} listen: 0.0.0.0:8080 + enable-encrypted-token: false + encryption-key: {{ .app.harness.secrets.gatekeeper | default (randAlphaNum 20) | quote }} enable-refresh-tokens: true 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 @@ -38,7 +40,6 @@ data: tls-cert: tls-private-key: redirection-url: {{ ternary "https" "http" $tls }}://{{ .subdomain }}.{{ .root.Values.domain }} - encryption-key: AgXa7xRcoClDEU0ZDSH4X0XhL5Qy2Z2j upstream-url: http://{{ .app.harness.service.name }}.{{ .app.namespace | default .root.Release.Namespace }}:{{ .app.harness.service.port | default 80}} {{ if .app.harness.secured }} {{ with .app.harness.uri_role_mapping }} @@ -135,7 +136,7 @@ spec: {{ include "deploy_utils.etcHosts" .root | indent 6 }} containers: - name: {{ .app.harness.service.name | quote }} - image: {{ .app.harness.proxy.gatekeeper.image | default .root.Values.proxy.gatekeeper.image | default "quay.io/gogatekeeper/gatekeeper:2.14.3" }} + image: {{ .app.harness.proxy.gatekeeper.image | default .root.Values.proxy.gatekeeper.image | default "quay.io/gogatekeeper/gatekeeper:4.6.0" }} imagePullPolicy: IfNotPresent {{ if .root.Values.local }} securityContext: diff --git a/deployment-configuration/value-template.yaml b/deployment-configuration/value-template.yaml index f02e03e5..03e2f5cb 100644 --- a/deployment-configuration/value-template.yaml +++ b/deployment-configuration/value-template.yaml @@ -55,7 +55,8 @@ harness: # -- Service port. port: 80 # -- Auto generated secrets key-value pairs. If no value is provided, a random hash is generated - secrets: {} + secrets: + gatekeeper: # -- Specify which services this application uses in the frontend to create proxy ingresses. e.g. - name: mnp-checkout use_services: [] # -- enabled sentry for automated error report From 382e4c3326a605984d650552600e4866daff812d Mon Sep 17 00:00:00 2001 From: Ludal Date: Mon, 9 Mar 2026 09:23:56 +0100 Subject: [PATCH 10/33] feat: define annotations based on ingress class --- .../helm/templates/certs/letsencrypt.yaml | 2 +- .../helm/templates/ingress.yaml | 11 ++++-- .../helm/templates/traefik-middlewares.yaml | 35 +++++++++++++++++++ deployment-configuration/helm/values.yaml | 4 +++ 4 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 deployment-configuration/helm/templates/traefik-middlewares.yaml diff --git a/deployment-configuration/helm/templates/certs/letsencrypt.yaml b/deployment-configuration/helm/templates/certs/letsencrypt.yaml index be45ac34..7135e79c 100644 --- a/deployment-configuration/helm/templates/certs/letsencrypt.yaml +++ b/deployment-configuration/helm/templates/certs/letsencrypt.yaml @@ -12,5 +12,5 @@ spec: solvers: - http01: ingress: - class: nginx + class: {{ .Values.ingress.ingressClass }} {{ end }} diff --git a/deployment-configuration/helm/templates/ingress.yaml b/deployment-configuration/helm/templates/ingress.yaml index ea001f7a..b48309a4 100644 --- a/deployment-configuration/helm/templates/ingress.yaml +++ b/deployment-configuration/helm/templates/ingress.yaml @@ -70,11 +70,12 @@ kind: Ingress metadata: name: {{ .Values.ingress.name | quote }} annotations: - kubernetes.io/ingress.class: nginx # Deprecated by Kubernetes, however still required for GKE + kubernetes.io/ingress.class: {{ .Values.ingress.ingressClass }} # Deprecated by Kubernetes, however still required for GKE {{- if and (not .Values.local) $tls }} kubernetes.io/tls-acme: 'true' cert-manager.io/issuer: {{ printf "%s-%s" "letsencrypt" .Values.namespace }} {{- end }} + {{- if eq .Values.ingress.ingressClass "nginx" }} nginx.ingress.kubernetes.io/ssl-redirect: {{ (and $tls .Values.ingress.ssl_redirect) | quote }} nginx.ingress.kubernetes.io/proxy-body-size: '{{ .Values.proxy.payload.max }}m' nginx.ingress.kubernetes.io/proxy-buffer-size: '128k' @@ -87,9 +88,15 @@ metadata: nginx.ingress.kubernetes.io/proxy-read-timeout: {{ .Values.proxy.timeout.read | quote }} nginx.ingress.kubernetes.io/proxy-send-timeout: {{ .Values.proxy.timeout.send | quote }} nginx.ingress.kubernetes.io/use-forwarded-headers: {{ .Values.proxy.forwardedHeaders | quote }} + {{- else if eq .Values.ingress.ingressClass "traefik" }} traefik.ingress.kubernetes.io/router.pathmatcher: "PathRegexp" + traefik.ingress.kubernetes.io/router.middlewares: {{ printf "%s-ch-rewrite-target@kubernetescrd,%s-ch-body-size@kubernetescrd" .Values.namespace .Values.namespace }}{{ if and $tls .Values.ingress.ssl_redirect }},{{ printf "%s-ch-ssl-redirect@kubernetescrd" .Values.namespace }}{{ end }} + {{- end }} + {{- with .Values.ingress.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} spec: - ingressClassName: nginx + ingressClassName: {{ .Values.ingress.ingressClass }} rules: {{- range $app := .Values.apps }} {{- if (and $mainapp (and $app.harness.name (eq $app.harness.name $mainapp))) }} diff --git a/deployment-configuration/helm/templates/traefik-middlewares.yaml b/deployment-configuration/helm/templates/traefik-middlewares.yaml new file mode 100644 index 00000000..42b600e2 --- /dev/null +++ b/deployment-configuration/helm/templates/traefik-middlewares.yaml @@ -0,0 +1,35 @@ +{{- if and .Values.ingress.enabled (eq .Values.ingress.ingressClass "traefik") }} +{{ $tls := not (not .Values.tls) }} +# Rewrite target: equivalent of nginx.ingress.kubernetes.io/rewrite-target: /$1 +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: ch-rewrite-target +spec: + replacePathRegex: + regex: "^/[^/]+(.*)" + replacement: "/$1" +--- +# Body size / buffering: equivalent of nginx proxy-body-size, proxy-buffer-size +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: ch-body-size +spec: + buffering: + maxRequestBodyBytes: {{ mul .Values.proxy.payload.max 1048576 }} + memRequestBodyBytes: 131072 + memResponseBodyBytes: 131072 +{{- if and $tls .Values.ingress.ssl_redirect }} +--- +# SSL redirect: equivalent of nginx.ingress.kubernetes.io/ssl-redirect +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: ch-ssl-redirect +spec: + redirectScheme: + scheme: https + permanent: true +{{- end }} +{{- end }} diff --git a/deployment-configuration/helm/values.yaml b/deployment-configuration/helm/values.yaml index 8468b96a..c92544f8 100644 --- a/deployment-configuration/helm/values.yaml +++ b/deployment-configuration/helm/values.yaml @@ -35,6 +35,10 @@ ingress: enabled: true # -- K8s Name of ingress. name: cloudharness-ingress + # -- The ingress class to use (e.g. "nginx", "traefik"). + ingressClass: nginx + # -- Additional custom annotations to add to the Ingress resource. + annotations: {} # -- Enables/disables SSL redirect. ssl_redirect: true letsencrypt: From ee02caea9b9bdde1541bfe6c2fa83f66fc82182c Mon Sep 17 00:00:00 2001 From: Filippo Ledda Date: Mon, 9 Mar 2026 16:33:06 +0100 Subject: [PATCH 11/33] CH-243 support for traefik routes --- .../helm/templates/ingress.yaml | 222 +++++++++++------- .../helm/templates/traefik-middlewares.yaml | 10 + deployment-configuration/helm/values.yaml | 4 +- deployment-configuration/value-template.yaml | 7 +- docs/model/ApplicationHarnessConfig.md | 1 + docs/model/GatewayConfig.md | 31 +++ docs/model/HarnessMainConfig.md | 2 +- docs/model/IngressConfig.md | 34 --- docs/model/IngressConfigAllOfLetsencrypt.md | 30 --- docs/model/IngressGlobalConfig.md | 36 +++ .../IngressGlobalConfigAllOfLetsencrypt.md | 30 +++ libraries/models/README.md | 5 +- libraries/models/api/openapi.yaml | 74 ++++-- .../cloudharness_model/models/__init__.py | 5 +- .../models/application_harness_config.py | 10 +- .../models/gateway_config.py | 85 +++++++ .../models/harness_main_config.py | 6 +- .../models/ingress_config.py | 20 +- .../models/ingress_global_config.py | 99 ++++++++ ...ngress_global_config_all_of_letsencrypt.py | 83 +++++++ .../models/docs/ApplicationHarnessConfig.md | 1 + libraries/models/docs/GatewayConfig.md | 31 +++ libraries/models/docs/HarnessMainConfig.md | 2 +- libraries/models/docs/IngressConfig.md | 7 +- libraries/models/docs/IngressGlobalConfig.md | 36 +++ .../IngressGlobalConfigAllOfLetsencrypt.md | 30 +++ 26 files changed, 698 insertions(+), 203 deletions(-) create mode 100644 docs/model/GatewayConfig.md delete mode 100644 docs/model/IngressConfig.md delete mode 100644 docs/model/IngressConfigAllOfLetsencrypt.md create mode 100644 docs/model/IngressGlobalConfig.md create mode 100644 docs/model/IngressGlobalConfigAllOfLetsencrypt.md create mode 100644 libraries/models/cloudharness_model/models/gateway_config.py create mode 100644 libraries/models/cloudharness_model/models/ingress_global_config.py create mode 100644 libraries/models/cloudharness_model/models/ingress_global_config_all_of_letsencrypt.py create mode 100644 libraries/models/docs/GatewayConfig.md create mode 100644 libraries/models/docs/IngressGlobalConfig.md create mode 100644 libraries/models/docs/IngressGlobalConfigAllOfLetsencrypt.md diff --git a/deployment-configuration/helm/templates/ingress.yaml b/deployment-configuration/helm/templates/ingress.yaml index 938f46ca..1db0c2df 100644 --- a/deployment-configuration/helm/templates/ingress.yaml +++ b/deployment-configuration/helm/templates/ingress.yaml @@ -1,59 +1,60 @@ {{- define "deploy_utils.ingress.http" }} + {{ $root := .root }} {{ $domain := .root.Values.domain }} {{ $secured_gatekeepers := and .root.Values.secured_gatekeepers }} {{ $app := .app }} + {{ $subdomain := .subdomain }} http: paths: -{{- if and $app.harness.secured $secured_gatekeepers $app.harness.uri_role_mapping }} - {{- range $mapping := $app.harness.uri_role_mapping }} - {{- if and (hasKey $mapping "white-listed") (index $mapping "white-listed") }} - {{- $uri := $mapping.uri }} - {{- if eq $uri "/" }} - - path: /() - pathType: ImplementationSpecific + {{- if and $app.harness.secured $secured_gatekeepers $app.harness.uri_role_mapping (eq $app.harness.gateway.pathType "Prefix") }} + {{- range $mapping := $app.harness.uri_role_mapping }} + {{- if and (hasKey $mapping "white-listed") (index $mapping "white-listed") }} + {{- $uri := $mapping.uri }} + {{- if eq $uri "/" }} + - path: / + pathType: {{ $app.harness.gateway.pathType | default $root.Values.ingress.pathType | default "Prefix" }} backend: service: name: {{ $app.harness.service.name | quote }} port: number: {{ $app.harness.service.port | default 80 }} - {{- else if hasSuffix "/*" $uri }} - {{- $cleanPath := trimSuffix "/*" $uri }} - {{- $pathWithoutSlash := trimPrefix "/" $cleanPath }} - - path: {{ printf "/(%s/.*)" $pathWithoutSlash }} - pathType: ImplementationSpecific + {{- else if hasSuffix "/*" $uri }} + {{- $cleanPath := trimSuffix "/*" $uri }} + {{- $pathWithoutSlash := trimPrefix "/" $cleanPath }} + - path: {{ printf "/%s/" $pathWithoutSlash }} + pathType: Prefix backend: service: name: {{ $app.harness.service.name | quote }} port: number: {{ $app.harness.service.port | default 80 }} - {{- else if not (contains "*" $uri) }} - {{- $pathWithoutSlash := trimPrefix "/" $uri }} - - path: {{ printf "/(%s)" $pathWithoutSlash }} - pathType: ImplementationSpecific + {{- else if (not (contains "*" $uri)) }} + {{- $pathWithoutSlash := trimPrefix "/" $uri }} + - path: {{ printf "%s/%s" $app.harness.gateway.path $pathWithoutSlash }} + pathType: Prefix backend: service: name: {{ $app.harness.service.name | quote }} port: number: {{ $app.harness.service.port | default 80 }} + {{- end }} + {{- end }} + {{- end }} {{- end }} - {{- end }} - {{- end }} -{{- end }} - - path: {{ default .root.Values.ingress.route_pattern $app.harness.route_pattern }} - pathType: ImplementationSpecific + - path: {{ $app.harness.gateway.path | default $root.Values.ingress.path | default "/" }} + pathType: {{ $app.harness.gateway.pathType | default $root.Values.ingress.pathType | default "Prefix" }} backend: service: - name: {{ if (and $app.harness.secured $secured_gatekeepers) }}{{ printf "%s-gk" .subdomain }}{{ else }}{{ $app.harness.service.name | quote }}{{ end }} + name: {{ if (and $app.harness.secured $secured_gatekeepers) }}{{ printf "%s-gk" $subdomain }}{{ else }}{{ $app.harness.service.name | quote }}{{ end }} port: - number: {{- if (and $app.harness.secured $secured_gatekeepers) }} 8080 {{- else }} {{ $app.harness.service.port | default 80 }}{{- end }} - + number: {{- if (and $app.harness.secured $secured_gatekeepers) }} 8080 {{- else }} {{ $app.harness.service.port | default 80 }}{{- end }} {{- end }} {{- define "deploy_utils.ingress.service" }} {{ $domain := .root.Values.domain }} {{ $secured_gatekeepers := and .root.Values.secured_gatekeepers }} {{ $app := get .root.Values.apps .service_name}} - {{ $routePattern := default .root.Values.ingress.route_pattern $app.harness.route_pattern }} - - path: /proxy/{{ $app.harness.service.name }}{{ $routePattern }} + {{ $routePattern := default .root.Values.ingress.path $app.harness.gateway.path }} + - path: /proxy/{{ $app.harness.service.name }}/{{- if eq .root.Values.ingress.ingressClass "nginx" }}(.*){{- else }}.*{{- end }} pathType: ImplementationSpecific backend: service: @@ -62,100 +63,155 @@ number: {{- if (and $app.harness.secured $secured_gatekeepers) }} 8080 {{- else }} {{ $app.harness.service.port | default 80 }}{{- end }} {{- end }} {{- if .Values.ingress.enabled }} -{{ $domain := .Values.domain }} -{{ $tls := not (not .Values.tls) }} -{{ $mainapp := .Values.mainapp }} + {{- $domain := .Values.domain }} + {{- $tls := not (not .Values.tls) }} + {{- $mainapp := .Values.mainapp }} + {{- range $app := .Values.apps }} + {{- if or $app.harness.subdomain $app.harness.domain $app.harness.aliases (and $mainapp (and $app.harness.name (eq $app.harness.name $mainapp))) }} + {{- $appIngressName := default $app.harness.name $app.harness.service.name }} +--- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - name: {{ .Values.ingress.name | quote }} + name: {{ $appIngressName | quote }} annotations: - kubernetes.io/ingress.class: {{ .Values.ingress.ingressClass }} # Deprecated by Kubernetes, however still required for GKE - {{- if and (not .Values.local) $tls }} + kubernetes.io/ingress.class: {{ $.Values.ingress.ingressClass }} # Deprecated by Kubernetes, however still required for GKE + {{- if and (not $.Values.local) $tls }} kubernetes.io/tls-acme: 'true' - cert-manager.io/issuer: {{ printf "%s-%s" "letsencrypt" .Values.namespace }} + cert-manager.io/issuer: {{ printf "%s-%s" "letsencrypt" $.Values.namespace }} {{- end }} - {{- if eq .Values.ingress.ingressClass "nginx" }} - nginx.ingress.kubernetes.io/ssl-redirect: {{ (and $tls .Values.ingress.ssl_redirect) | quote }} - nginx.ingress.kubernetes.io/proxy-body-size: '{{ .Values.proxy.payload.max }}m' + {{- if eq $.Values.ingress.ingressClass "nginx" }} + nginx.ingress.kubernetes.io/ssl-redirect: {{ (and $tls $.Values.ingress.ssl_redirect) | quote }} + nginx.ingress.kubernetes.io/proxy-body-size: '{{ $.Values.proxy.payload.max }}m' nginx.ingress.kubernetes.io/proxy-buffer-size: '128k' nginx.ingress.kubernetes.io/proxy-buffering: "on" nginx.ingress.kubernetes.io/proxy-buffers-number: "8" nginx.ingress.kubernetes.io/proxy-max-temp-file-size: "1024m" 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 }} - nginx.ingress.kubernetes.io/use-forwarded-headers: {{ .Values.proxy.forwardedHeaders | quote }} - {{- else if eq .Values.ingress.ingressClass "traefik" }} - traefik.ingress.kubernetes.io/router.pathmatcher: "PathRegexp" - traefik.ingress.kubernetes.io/router.middlewares: {{ printf "%s-ch-rewrite-target@kubernetescrd,%s-ch-body-size@kubernetescrd" .Values.namespace .Values.namespace }}{{ if and $tls .Values.ingress.ssl_redirect }},{{ printf "%s-ch-ssl-redirect@kubernetescrd" .Values.namespace }}{{ end }} + 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 }} + nginx.ingress.kubernetes.io/use-forwarded-headers: {{ $.Values.proxy.forwardedHeaders | quote }} + {{- else if eq $.Values.ingress.ingressClass "traefik" }} + traefik.ingress.kubernetes.io/router.middlewares: {{ printf "%s-ch-body-size@kubernetescrd" $.Values.namespace }}{{ if and $tls $.Values.ingress.ssl_redirect }},{{ printf "%s-ch-ssl-redirect@kubernetescrd" $.Values.namespace }}{{ end }} {{- end }} - {{- with .Values.ingress.annotations }} + {{- with $.Values.ingress.annotations }} {{- toYaml . | nindent 4 }} {{- end }} spec: - ingressClassName: {{ .Values.ingress.ingressClass }} + ingressClassName: {{ $.Values.ingress.ingressClass }} rules: - {{- range $app := .Values.apps }} - {{- if (and $mainapp (and $app.harness.name (eq $app.harness.name $mainapp))) }} - {{- if ne $app.harness.subdomain "www" }} + {{- if (and $mainapp (and $app.harness.name (eq $app.harness.name $mainapp))) }} + {{- if ne $app.harness.subdomain "www" }} - host: {{ $domain | quote }} {{ include "deploy_utils.ingress.http" (dict "root" $ "app" $app "subdomain" $app.harness.subdomain) }} - {{- range $service := $app.harness.use_services }} - {{ include "deploy_utils.ingress.service" (dict "root" $ "service_name" $service.name) }} - {{- end }} + {{- end }} {{- end }} - {{- end }} - {{- if $app.harness.domain }} + {{- if $app.harness.domain }} - host: {{ $app.harness.domain | quote }} - {{ include "deploy_utils.ingress.http" (dict "root" $ "app" $app "subdomain" $app.harness.subdomain ) }} - {{- end }} - {{- if $app.harness.aliases }} - {{- range $alias := $app.harness.aliases }} + {{ include "deploy_utils.ingress.http" (dict "root" $ "app" $app "subdomain" $app.harness.subdomain) }} + {{- end }} + {{- if $app.harness.aliases }} + {{- range $alias := $app.harness.aliases }} - host: {{ printf "%s.%s" $alias $domain | quote }} - {{ include "deploy_utils.ingress.http" (dict "root" $ "app" $app "subdomain" $alias) }} + {{ include "deploy_utils.ingress.http" (dict "root" $ "app" $app "subdomain" $alias) }} + {{- end }} {{- end }} - {{- end }} - {{- if $app.harness.subdomain }} + {{- if $app.harness.subdomain }} - host: {{ printf "%s.%s" $app.harness.subdomain $domain | quote }} - {{ include "deploy_utils.ingress.http" (dict "root" $ "app" $app "subdomain" $app.harness.subdomain) }} - {{- range $service := $app.harness.use_services }} - {{ include "deploy_utils.ingress.service" (dict "root" $ "service_name" $service.name) }} - {{- end }} - {{- range $subapp := $app }} - {{- if contains "map" (typeOf $subapp) }} - {{- if and $subapp (hasKey $subapp "harness.subdomain") }} + {{ include "deploy_utils.ingress.http" (dict "root" $ "app" $app "subdomain" $app.harness.subdomain) }} + {{- range $subapp := $app }} + {{- if contains "map" (typeOf $subapp) }} + {{- if and $subapp (hasKey $subapp "harness.subdomain") }} - host: {{ printf "%s.%s.%s" $subapp.harness.subdomain $app.harness.subdomain $domain | quote }} {{ include "deploy_utils.ingress.http" (dict "root" $ "app" $subapp "subdomain" (printf "%s.%s" $subapp.harness.subdomain $app.harness.subdomain)) }} + {{- end }} {{- end }} - {{- end }} + {{- end }} {{- end }} - {{- end }} - - {{- end }} - {{- if $tls }} + {{- if $tls }} tls: - hosts: - {{- range $app := .Values.apps }} {{- if $app.harness.subdomain }} - {{ printf "%s.%s" $app.harness.subdomain $domain | quote }} {{- end }} - {{- if $app.harness.domain }} - {{- if ne $app.harness.domain $domain }} + {{- if $app.harness.domain }} + {{- if ne $app.harness.domain $domain }} - {{ $app.harness.domain | quote }} - {{- end }} - {{- end }} - {{- if $app.harness.aliases }} - {{- range $subdomain := $app.harness.aliases }} + {{- end }} + {{- end }} + {{- if $app.harness.aliases }} + {{- range $subdomain := $app.harness.aliases }} - {{ printf "%s.%s" $subdomain $domain | quote }} + {{- end }} {{- end }} - {{- end }} - {{- end }} - {{- if $mainapp }} + {{- if (and $mainapp (and $app.harness.name (eq $app.harness.name $mainapp))) }} + {{- if ne $app.harness.subdomain "www" }} - {{ $domain | quote }} + {{- end }} + {{- end }} + secretName: tls-secret {{- end }} + {{- if $app.harness.use_services }} +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ printf "%s-proxy" $appIngressName | quote }} + annotations: + kubernetes.io/ingress.class: {{ $.Values.ingress.ingressClass }} # Deprecated by Kubernetes, however still required for GKE + {{- if and (not $.Values.local) $tls }} + kubernetes.io/tls-acme: 'true' + cert-manager.io/issuer: {{ printf "%s-%s" "letsencrypt" $.Values.namespace }} + {{- end }} + {{- if eq $.Values.ingress.ingressClass "nginx" }} + nginx.ingress.kubernetes.io/rewrite-target: /$1 + nginx.ingress.kubernetes.io/proxy-body-size: '{{ $.Values.proxy.payload.max }}m' + nginx.ingress.kubernetes.io/proxy-read-timeout: {{ $.Values.proxy.timeout.read | quote }} + nginx.ingress.kubernetes.io/proxy-send-timeout: {{ $.Values.proxy.timeout.send | quote }} + nginx.ingress.kubernetes.io/use-forwarded-headers: {{ $.Values.proxy.forwardedHeaders | quote }} + {{- else if eq $.Values.ingress.ingressClass "traefik" }} + traefik.ingress.kubernetes.io/router.pathmatcher: "PathRegexp" + traefik.ingress.kubernetes.io/router.middlewares: {{ printf "%s-ch-proxy-rewrite@kubernetescrd,%s-ch-body-size@kubernetescrd" $.Values.namespace $.Values.namespace }}{{ if and $tls $.Values.ingress.ssl_redirect }},{{ printf "%s-ch-ssl-redirect@kubernetescrd" $.Values.namespace }}{{ end }} + {{- end }} + {{- with $.Values.ingress.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + ingressClassName: {{ $.Values.ingress.ingressClass }} + rules: + {{- if (and $mainapp (and $app.harness.name (eq $app.harness.name $mainapp))) }} + {{- if ne $app.harness.subdomain "www" }} + - host: {{ $domain | quote }} + http: + paths: + {{- range $service := $app.harness.use_services }} + {{ include "deploy_utils.ingress.service" (dict "root" $ "service_name" $service.name) }} + {{- end }} + {{- end }} + {{- end }} + {{- if $app.harness.subdomain }} + - host: {{ printf "%s.%s" $app.harness.subdomain $domain | quote }} + http: + paths: + {{- range $service := $app.harness.use_services }} + {{ include "deploy_utils.ingress.service" (dict "root" $ "service_name" $service.name) }} + {{- end }} + {{- end }} + {{- if $tls }} + tls: + - hosts: + {{- if $app.harness.subdomain }} + - {{ printf "%s.%s" $app.harness.subdomain $domain | quote }} + {{- end }} + {{- if (and $mainapp (and $app.harness.name (eq $app.harness.name $mainapp))) }} + {{- if ne $app.harness.subdomain "www" }} + - {{ $domain | quote }} + {{- end }} + {{- end }} secretName: tls-secret + {{- end }} + {{- end }} + {{- end }} {{- end }} {{- end }} diff --git a/deployment-configuration/helm/templates/traefik-middlewares.yaml b/deployment-configuration/helm/templates/traefik-middlewares.yaml index 42b600e2..b3349384 100644 --- a/deployment-configuration/helm/templates/traefik-middlewares.yaml +++ b/deployment-configuration/helm/templates/traefik-middlewares.yaml @@ -10,6 +10,16 @@ spec: regex: "^/[^/]+(.*)" replacement: "/$1" --- +# Proxy rewrite: strips /proxy/ prefix for use_services proxy routes +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: ch-proxy-rewrite +spec: + replacePathRegex: + regex: "^/proxy/[^/]+(.*)" + replacement: "$1" +--- # Body size / buffering: equivalent of nginx proxy-body-size, proxy-buffer-size apiVersion: traefik.io/v1alpha1 kind: Middleware diff --git a/deployment-configuration/helm/values.yaml b/deployment-configuration/helm/values.yaml index c92544f8..9d3cf516 100644 --- a/deployment-configuration/helm/values.yaml +++ b/deployment-configuration/helm/values.yaml @@ -45,7 +45,9 @@ ingress: # -- Email for letsencrypt. email: cloudharness@metacell.us # -- Default regex segment for routes (used in paths like '/(pattern)'). - route_pattern: "/(.*)" + path: "/" + # -- The pathType for the Ingress path. Default is Prefix. For regex paths, set to ImplementationSpecific + pathType: Prefix backup: # -- Flag to enable/disable backups. active: false diff --git a/deployment-configuration/value-template.yaml b/deployment-configuration/value-template.yaml index 03e2f5cb..6d273d9d 100644 --- a/deployment-configuration/value-template.yaml +++ b/deployment-configuration/value-template.yaml @@ -148,4 +148,9 @@ harness: requests: memory: "32Mi" limits: - memory: "64Mi" \ No newline at end of file + memory: "64Mi" + gateway: + ## -- The path mapped to the current application in the gateway. Default is "/". For regex paths, use the path specified in .Values.ingress.path and set pathType to ImplementationSpecific + path: "/" + ## -- The pathType for the Ingress path. Default is Prefix. For regex paths, set to ImplementationSpecific + pathType: Prefix \ No newline at end of file diff --git a/docs/model/ApplicationHarnessConfig.md b/docs/model/ApplicationHarnessConfig.md index a589c11a..f10c624f 100644 --- a/docs/model/ApplicationHarnessConfig.md +++ b/docs/model/ApplicationHarnessConfig.md @@ -33,6 +33,7 @@ Name | Type | Description | Notes **sentry** | **bool** | | [optional] **proxy** | [**ProxyConf**](ProxyConf.md) | | [optional] **image_name** | **str** | Use this name for the image in place of the default directory name | [optional] +**gateway** | [**GatewayConfig**](GatewayConfig.md) | | [optional] ## Example diff --git a/docs/model/GatewayConfig.md b/docs/model/GatewayConfig.md new file mode 100644 index 00000000..c22eea8d --- /dev/null +++ b/docs/model/GatewayConfig.md @@ -0,0 +1,31 @@ +# GatewayConfig + + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**path_type** | **str** | Ingress path type | +**path** | **str** | Default target path prefix for applications endpoints. To use regular expressions (e.g.'/(pattern)'), also set `route_type` to `ImplementationSpecific`. | + +## Example + +```python +from cloudharness_model.models.gateway_config import GatewayConfig + +# TODO update the JSON string below +json = "{}" +# create an instance of GatewayConfig from a JSON string +gateway_config_instance = GatewayConfig.from_json(json) +# print the JSON string representation of the object +print(GatewayConfig.to_json()) + +# convert the object into a dict +gateway_config_dict = gateway_config_instance.to_dict() +# create an instance of GatewayConfig from a dict +gateway_config_from_dict = GatewayConfig.from_dict(gateway_config_dict) +``` +[[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/HarnessMainConfig.md b/docs/model/HarnessMainConfig.md index 7940b51c..f9f22fe2 100644 --- a/docs/model/HarnessMainConfig.md +++ b/docs/model/HarnessMainConfig.md @@ -20,7 +20,7 @@ Name | Type | Description | Notes **name** | **str** | Base name | [optional] **task_images** | **Dict[str, object]** | | [optional] **build_hash** | **str** | | [optional] -**ingress** | [**IngressConfig**](IngressConfig.md) | | [optional] +**ingress** | [**IngressGlobalConfig**](IngressGlobalConfig.md) | | [optional] ## Example diff --git a/docs/model/IngressConfig.md b/docs/model/IngressConfig.md deleted file mode 100644 index 5e2add2f..00000000 --- a/docs/model/IngressConfig.md +++ /dev/null @@ -1,34 +0,0 @@ -# IngressConfig - - - -## Properties - -Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- -**auto** | **bool** | When true, enables automatic template | [optional] -**name** | **str** | | [optional] -**ssl_redirect** | **bool** | | [optional] -**letsencrypt** | [**IngressConfigAllOfLetsencrypt**](IngressConfigAllOfLetsencrypt.md) | | [optional] -**enabled** | **bool** | | [optional] - -## Example - -```python -from cloudharness_model.models.ingress_config import IngressConfig - -# TODO update the JSON string below -json = "{}" -# create an instance of IngressConfig from a JSON string -ingress_config_instance = IngressConfig.from_json(json) -# print the JSON string representation of the object -print(IngressConfig.to_json()) - -# convert the object into a dict -ingress_config_dict = ingress_config_instance.to_dict() -# create an instance of IngressConfig from a dict -ingress_config_from_dict = IngressConfig.from_dict(ingress_config_dict) -``` -[[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/IngressConfigAllOfLetsencrypt.md b/docs/model/IngressConfigAllOfLetsencrypt.md deleted file mode 100644 index fa4ec090..00000000 --- a/docs/model/IngressConfigAllOfLetsencrypt.md +++ /dev/null @@ -1,30 +0,0 @@ -# IngressConfigAllOfLetsencrypt - - - -## Properties - -Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- -**email** | **str** | | [optional] - -## Example - -```python -from cloudharness_model.models.ingress_config_all_of_letsencrypt import IngressConfigAllOfLetsencrypt - -# TODO update the JSON string below -json = "{}" -# create an instance of IngressConfigAllOfLetsencrypt from a JSON string -ingress_config_all_of_letsencrypt_instance = IngressConfigAllOfLetsencrypt.from_json(json) -# print the JSON string representation of the object -print(IngressConfigAllOfLetsencrypt.to_json()) - -# convert the object into a dict -ingress_config_all_of_letsencrypt_dict = ingress_config_all_of_letsencrypt_instance.to_dict() -# create an instance of IngressConfigAllOfLetsencrypt from a dict -ingress_config_all_of_letsencrypt_from_dict = IngressConfigAllOfLetsencrypt.from_dict(ingress_config_all_of_letsencrypt_dict) -``` -[[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/IngressGlobalConfig.md b/docs/model/IngressGlobalConfig.md new file mode 100644 index 00000000..02300510 --- /dev/null +++ b/docs/model/IngressGlobalConfig.md @@ -0,0 +1,36 @@ +# IngressGlobalConfig + + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**auto** | **bool** | When true, enables automatic template | [optional] +**name** | **str** | | [optional] +**path_type** | **str** | Ingress path type | +**path** | **str** | Default target path prefix for applications endpoints. To use regular expressions (e.g.'/(pattern)'), also set `route_type` to `ImplementationSpecific`. | +**ssl_redirect** | **bool** | | [optional] +**letsencrypt** | [**IngressGlobalConfigAllOfLetsencrypt**](IngressGlobalConfigAllOfLetsencrypt.md) | | [optional] +**enabled** | **bool** | | [optional] + +## Example + +```python +from cloudharness_model.models.ingress_global_config import IngressGlobalConfig + +# TODO update the JSON string below +json = "{}" +# create an instance of IngressGlobalConfig from a JSON string +ingress_global_config_instance = IngressGlobalConfig.from_json(json) +# print the JSON string representation of the object +print(IngressGlobalConfig.to_json()) + +# convert the object into a dict +ingress_global_config_dict = ingress_global_config_instance.to_dict() +# create an instance of IngressGlobalConfig from a dict +ingress_global_config_from_dict = IngressGlobalConfig.from_dict(ingress_global_config_dict) +``` +[[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/IngressGlobalConfigAllOfLetsencrypt.md b/docs/model/IngressGlobalConfigAllOfLetsencrypt.md new file mode 100644 index 00000000..f376f137 --- /dev/null +++ b/docs/model/IngressGlobalConfigAllOfLetsencrypt.md @@ -0,0 +1,30 @@ +# IngressGlobalConfigAllOfLetsencrypt + + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**email** | **str** | | [optional] + +## Example + +```python +from cloudharness_model.models.ingress_global_config_all_of_letsencrypt import IngressGlobalConfigAllOfLetsencrypt + +# TODO update the JSON string below +json = "{}" +# create an instance of IngressGlobalConfigAllOfLetsencrypt from a JSON string +ingress_global_config_all_of_letsencrypt_instance = IngressGlobalConfigAllOfLetsencrypt.from_json(json) +# print the JSON string representation of the object +print(IngressGlobalConfigAllOfLetsencrypt.to_json()) + +# convert the object into a dict +ingress_global_config_all_of_letsencrypt_dict = ingress_global_config_all_of_letsencrypt_instance.to_dict() +# create an instance of IngressGlobalConfigAllOfLetsencrypt from a dict +ingress_global_config_all_of_letsencrypt_from_dict = IngressGlobalConfigAllOfLetsencrypt.from_dict(ingress_global_config_all_of_letsencrypt_dict) +``` +[[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/libraries/models/README.md b/libraries/models/README.md index a91663a5..466eb6ab 100644 --- a/libraries/models/README.md +++ b/libraries/models/README.md @@ -88,10 +88,11 @@ Class | Method | HTTP request | Description - [E2ETestsConfig](docs/E2ETestsConfig.md) - [FileResourcesConfig](docs/FileResourcesConfig.md) - [GatekeeperConf](docs/GatekeeperConf.md) + - [GatewayConfig](docs/GatewayConfig.md) - [GitDependencyConfig](docs/GitDependencyConfig.md) - [HarnessMainConfig](docs/HarnessMainConfig.md) - - [IngressConfig](docs/IngressConfig.md) - - [IngressConfigAllOfLetsencrypt](docs/IngressConfigAllOfLetsencrypt.md) + - [IngressGlobalConfig](docs/IngressGlobalConfig.md) + - [IngressGlobalConfigAllOfLetsencrypt](docs/IngressGlobalConfigAllOfLetsencrypt.md) - [JupyterHubConfig](docs/JupyterHubConfig.md) - [NameValue](docs/NameValue.md) - [NamedObject](docs/NamedObject.md) diff --git a/libraries/models/api/openapi.yaml b/libraries/models/api/openapi.yaml index b22a4fd2..fb02410b 100644 --- a/libraries/models/api/openapi.yaml +++ b/libraries/models/api/openapi.yaml @@ -126,6 +126,7 @@ components: additionalProperties: true RegistrySecretConfig: description: '' + required: [] type: object properties: name: @@ -587,7 +588,7 @@ components: description: '' type: string ingress: - $ref: '#/components/schemas/IngressConfig' + $ref: '#/components/schemas/IngressGlobalConfig' description: '' additionalProperties: true Organization: @@ -876,28 +877,6 @@ components: additionalProperties: true example: name: a name - IngressConfig: - description: '' - type: object - allOf: - - - type: object - properties: - ssl_redirect: - description: '' - type: boolean - letsencrypt: - description: '' - type: object - properties: - email: - type: string - enabled: - description: '' - type: boolean - - - $ref: '#/components/schemas/AutoArtifactSpec' - additionalProperties: true GatekeeperConf: title: Root Type for GatekeeperConf description: '' @@ -1035,4 +1014,53 @@ components: image_name: description: Use this name for the image in place of the default directory name type: string + gateway: + $ref: '#/components/schemas/GatewayConfig' + description: '' + additionalProperties: true + IngressGlobalConfig: + description: '' + type: object + allOf: + - + type: object + properties: + ssl_redirect: + description: '' + type: boolean + letsencrypt: + description: '' + type: object + properties: + email: + type: string + enabled: + description: '' + type: boolean + - + $ref: '#/components/schemas/AutoArtifactSpec' + - + $ref: '#/components/schemas/GatewayConfig' + additionalProperties: true + GatewayConfig: + description: '' + type: object + allOf: + - + required: + - path + - pathType + type: object + properties: + pathType: + description: 'Ingress path type ' + type: string + example: '"Prefix"' + path: + description: | + Default target path prefix for applications endpoints. + To use regular expressions (e.g.'/(pattern)'), also set `route_type` to + `ImplementationSpecific`. + type: string + example: '"/"' additionalProperties: true diff --git a/libraries/models/cloudharness_model/models/__init__.py b/libraries/models/cloudharness_model/models/__init__.py index 2e564493..cf529ebb 100644 --- a/libraries/models/cloudharness_model/models/__init__.py +++ b/libraries/models/cloudharness_model/models/__init__.py @@ -35,10 +35,11 @@ from cloudharness_model.models.e2_e_tests_config import E2ETestsConfig from cloudharness_model.models.file_resources_config import FileResourcesConfig from cloudharness_model.models.gatekeeper_conf import GatekeeperConf +from cloudharness_model.models.gateway_config import GatewayConfig from cloudharness_model.models.git_dependency_config import GitDependencyConfig from cloudharness_model.models.harness_main_config import HarnessMainConfig -from cloudharness_model.models.ingress_config import IngressConfig -from cloudharness_model.models.ingress_config_all_of_letsencrypt import IngressConfigAllOfLetsencrypt +from cloudharness_model.models.ingress_global_config import IngressGlobalConfig +from cloudharness_model.models.ingress_global_config_all_of_letsencrypt import IngressGlobalConfigAllOfLetsencrypt from cloudharness_model.models.jupyter_hub_config import JupyterHubConfig from cloudharness_model.models.name_value import NameValue from cloudharness_model.models.named_object import NamedObject diff --git a/libraries/models/cloudharness_model/models/application_harness_config.py b/libraries/models/cloudharness_model/models/application_harness_config.py index f4a74d88..d886a593 100644 --- a/libraries/models/cloudharness_model/models/application_harness_config.py +++ b/libraries/models/cloudharness_model/models/application_harness_config.py @@ -33,6 +33,7 @@ from cloudharness_model.models.deployment_auto_artifact_config import DeploymentAutoArtifactConfig from cloudharness_model.models.dockerfile_config import DockerfileConfig from cloudharness_model.models.file_resources_config import FileResourcesConfig +from cloudharness_model.models.gateway_config import GatewayConfig from cloudharness_model.models.jupyter_hub_config import JupyterHubConfig from cloudharness_model.models.name_value import NameValue from cloudharness_model.models.named_object import NamedObject @@ -71,8 +72,9 @@ class ApplicationHarnessConfig(CloudHarnessBaseModel): sentry: Optional[StrictBool] = None proxy: Optional[ProxyConf] = None image_name: Optional[StrictStr] = Field(default=None, description="Use this name for the image in place of the default directory name") + gateway: Optional[GatewayConfig] = None additional_properties: Dict[str, Any] = {} - __properties: ClassVar[List[str]] = ["deployment", "service", "subdomain", "aliases", "domain", "dependencies", "secured", "uri_role_mapping", "secrets", "use_services", "database", "resources", "readinessProbe", "startupProbe", "livenessProbe", "sourceRoot", "name", "jupyterhub", "accounts", "test", "quotas", "env", "envmap", "dockerfile", "sentry", "proxy", "image_name"] + __properties: ClassVar[List[str]] = ["deployment", "service", "subdomain", "aliases", "domain", "dependencies", "secured", "uri_role_mapping", "secrets", "use_services", "database", "resources", "readinessProbe", "startupProbe", "livenessProbe", "sourceRoot", "name", "jupyterhub", "accounts", "test", "quotas", "env", "envmap", "dockerfile", "sentry", "proxy", "image_name", "gateway"] @field_validator('source_root') def source_root_validate_regular_expression(cls, value): @@ -168,6 +170,9 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of proxy if self.proxy: _dict['proxy'] = self.proxy.to_dict() + # override the default output from pydantic by calling `to_dict()` of gateway + if self.gateway: + _dict['gateway'] = self.gateway.to_dict() # puts key-value pairs in additional_properties in the top level if self.additional_properties is not None: for _key, _value in self.additional_properties.items(): @@ -216,7 +221,8 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "dockerfile": DockerfileConfig.from_dict(obj["dockerfile"]) if obj.get("dockerfile") is not None else None, "sentry": obj.get("sentry"), "proxy": ProxyConf.from_dict(obj["proxy"]) if obj.get("proxy") is not None else None, - "image_name": obj.get("image_name") + "image_name": obj.get("image_name"), + "gateway": GatewayConfig.from_dict(obj["gateway"]) if obj.get("gateway") is not None else None }) # store additional fields in additional_properties for _key in obj.keys(): diff --git a/libraries/models/cloudharness_model/models/gateway_config.py b/libraries/models/cloudharness_model/models/gateway_config.py new file mode 100644 index 00000000..634fa400 --- /dev/null +++ b/libraries/models/cloudharness_model/models/gateway_config.py @@ -0,0 +1,85 @@ +# coding: utf-8 + +""" + cloudharness + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 1.0.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from typing import Optional, Set +from typing_extensions import Self + + +from cloudharness_model.base_model import CloudHarnessBaseModel +from pydantic import BaseModel, Field, field_validator, StrictStr, StrictBool, StrictInt, StrictFloat +from typing import ClassVar, List, Dict, Any, Union, Optional, Annotated +import importlib + +class GatewayConfig(CloudHarnessBaseModel): + """ + + """ # noqa: E501 + path_type: StrictStr = Field(description="Ingress path type ", alias="pathType") + path: StrictStr = Field(description="Default target path prefix for applications endpoints. To use regular expressions (e.g.'/(pattern)'), also set `route_type` to `ImplementationSpecific`. ") + additional_properties: Dict[str, Any] = {} + __properties: ClassVar[List[str]] = ["pathType", "path"] + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + * Fields in `self.additional_properties` are added to the output dict. + """ + excluded_fields: Set[str] = set([ + "additional_properties", + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # puts key-value pairs in additional_properties in the top level + if self.additional_properties is not None: + for _key, _value in self.additional_properties.items(): + _dict[_key] = _value + + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of GatewayConfig from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "pathType": obj.get("pathType"), + "path": obj.get("path") + }) + # store additional fields in additional_properties + for _key in obj.keys(): + if _key not in cls.__properties: + _obj.additional_properties[_key] = obj.get(_key) + + return _obj + + diff --git a/libraries/models/cloudharness_model/models/harness_main_config.py b/libraries/models/cloudharness_model/models/harness_main_config.py index 58de5581..380e3ce6 100644 --- a/libraries/models/cloudharness_model/models/harness_main_config.py +++ b/libraries/models/cloudharness_model/models/harness_main_config.py @@ -27,7 +27,7 @@ import importlib from cloudharness_model.models.application_config import ApplicationConfig from cloudharness_model.models.backup_config import BackupConfig -from cloudharness_model.models.ingress_config import IngressConfig +from cloudharness_model.models.ingress_global_config import IngressGlobalConfig from cloudharness_model.models.name_value import NameValue from cloudharness_model.models.registry_config import RegistryConfig @@ -49,7 +49,7 @@ class HarnessMainConfig(CloudHarnessBaseModel): name: Optional[StrictStr] = Field(default=None, description="Base name") task_images: Optional[Dict[str, Any]] = Field(default=None, alias="task-images") build_hash: Optional[StrictStr] = None - ingress: Optional[IngressConfig] = None + ingress: Optional[IngressGlobalConfig] = None additional_properties: Dict[str, Any] = {} __properties: ClassVar[List[str]] = ["local", "secured_gatekeepers", "domain", "namespace", "mainapp", "registry", "tag", "apps", "env", "privenv", "backup", "name", "task-images", "build_hash", "ingress"] @@ -139,7 +139,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "name": obj.get("name"), "task-images": obj.get("task-images"), "build_hash": obj.get("build_hash"), - "ingress": IngressConfig.from_dict(obj["ingress"]) if obj.get("ingress") is not None else None + "ingress": IngressGlobalConfig.from_dict(obj["ingress"]) if obj.get("ingress") is not None else None }) # store additional fields in additional_properties for _key in obj.keys(): diff --git a/libraries/models/cloudharness_model/models/ingress_config.py b/libraries/models/cloudharness_model/models/ingress_config.py index 9e9154f5..7d9a3525 100644 --- a/libraries/models/cloudharness_model/models/ingress_config.py +++ b/libraries/models/cloudharness_model/models/ingress_config.py @@ -25,19 +25,15 @@ from pydantic import BaseModel, Field, field_validator, StrictStr, StrictBool, StrictInt, StrictFloat from typing import ClassVar, List, Dict, Any, Union, Optional, Annotated import importlib -from cloudharness_model.models.ingress_config_all_of_letsencrypt import IngressConfigAllOfLetsencrypt class IngressConfig(CloudHarnessBaseModel): """ """ # noqa: E501 - auto: Optional[StrictBool] = Field(default=None, description="When true, enables automatic template") - name: Optional[StrictStr] = None - ssl_redirect: Optional[StrictBool] = None - letsencrypt: Optional[IngressConfigAllOfLetsencrypt] = None - enabled: Optional[StrictBool] = None + path_type: StrictStr = Field(description="Ingress path type ", alias="pathType") + path: StrictStr = Field(description="Default target path prefix for applications endpoints. To use regular expressions (e.g.'/(pattern)'), also set `route_type` to `ImplementationSpecific`. ") additional_properties: Dict[str, Any] = {} - __properties: ClassVar[List[str]] = ["auto", "name", "ssl_redirect", "letsencrypt", "enabled"] + __properties: ClassVar[List[str]] = ["pathType", "path"] def to_dict(self) -> Dict[str, Any]: """Return the dictionary representation of the model using alias. @@ -59,9 +55,6 @@ def to_dict(self) -> Dict[str, Any]: exclude=excluded_fields, exclude_none=True, ) - # override the default output from pydantic by calling `to_dict()` of letsencrypt - if self.letsencrypt: - _dict['letsencrypt'] = self.letsencrypt.to_dict() # puts key-value pairs in additional_properties in the top level if self.additional_properties is not None: for _key, _value in self.additional_properties.items(): @@ -79,11 +72,8 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: return cls.model_validate(obj) _obj = cls.model_validate({ - "auto": obj.get("auto"), - "name": obj.get("name"), - "ssl_redirect": obj.get("ssl_redirect"), - "letsencrypt": IngressConfigAllOfLetsencrypt.from_dict(obj["letsencrypt"]) if obj.get("letsencrypt") is not None else None, - "enabled": obj.get("enabled") + "pathType": obj.get("pathType"), + "path": obj.get("path") }) # store additional fields in additional_properties for _key in obj.keys(): diff --git a/libraries/models/cloudharness_model/models/ingress_global_config.py b/libraries/models/cloudharness_model/models/ingress_global_config.py new file mode 100644 index 00000000..1bd279ba --- /dev/null +++ b/libraries/models/cloudharness_model/models/ingress_global_config.py @@ -0,0 +1,99 @@ +# coding: utf-8 + +""" + cloudharness + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 1.0.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from typing import Optional, Set +from typing_extensions import Self + + +from cloudharness_model.base_model import CloudHarnessBaseModel +from pydantic import BaseModel, Field, field_validator, StrictStr, StrictBool, StrictInt, StrictFloat +from typing import ClassVar, List, Dict, Any, Union, Optional, Annotated +import importlib +from cloudharness_model.models.ingress_global_config_all_of_letsencrypt import IngressGlobalConfigAllOfLetsencrypt + +class IngressGlobalConfig(CloudHarnessBaseModel): + """ + + """ # noqa: E501 + auto: Optional[StrictBool] = Field(default=None, description="When true, enables automatic template") + name: Optional[StrictStr] = None + path_type: StrictStr = Field(description="Ingress path type ", alias="pathType") + path: StrictStr = Field(description="Default target path prefix for applications endpoints. To use regular expressions (e.g.'/(pattern)'), also set `route_type` to `ImplementationSpecific`. ") + ssl_redirect: Optional[StrictBool] = None + letsencrypt: Optional[IngressGlobalConfigAllOfLetsencrypt] = None + enabled: Optional[StrictBool] = None + additional_properties: Dict[str, Any] = {} + __properties: ClassVar[List[str]] = ["auto", "name", "pathType", "path", "ssl_redirect", "letsencrypt", "enabled"] + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + * Fields in `self.additional_properties` are added to the output dict. + """ + excluded_fields: Set[str] = set([ + "additional_properties", + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of letsencrypt + if self.letsencrypt: + _dict['letsencrypt'] = self.letsencrypt.to_dict() + # puts key-value pairs in additional_properties in the top level + if self.additional_properties is not None: + for _key, _value in self.additional_properties.items(): + _dict[_key] = _value + + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of IngressGlobalConfig from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "auto": obj.get("auto"), + "name": obj.get("name"), + "pathType": obj.get("pathType"), + "path": obj.get("path"), + "ssl_redirect": obj.get("ssl_redirect"), + "letsencrypt": IngressGlobalConfigAllOfLetsencrypt.from_dict(obj["letsencrypt"]) if obj.get("letsencrypt") is not None else None, + "enabled": obj.get("enabled") + }) + # store additional fields in additional_properties + for _key in obj.keys(): + if _key not in cls.__properties: + _obj.additional_properties[_key] = obj.get(_key) + + return _obj + + diff --git a/libraries/models/cloudharness_model/models/ingress_global_config_all_of_letsencrypt.py b/libraries/models/cloudharness_model/models/ingress_global_config_all_of_letsencrypt.py new file mode 100644 index 00000000..871aa2fc --- /dev/null +++ b/libraries/models/cloudharness_model/models/ingress_global_config_all_of_letsencrypt.py @@ -0,0 +1,83 @@ +# coding: utf-8 + +""" + cloudharness + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 1.0.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from typing import Optional, Set +from typing_extensions import Self + + +from cloudharness_model.base_model import CloudHarnessBaseModel +from pydantic import BaseModel, Field, field_validator, StrictStr, StrictBool, StrictInt, StrictFloat +from typing import ClassVar, List, Dict, Any, Union, Optional, Annotated +import importlib + +class IngressGlobalConfigAllOfLetsencrypt(CloudHarnessBaseModel): + """ + + """ # noqa: E501 + email: Optional[StrictStr] = None + additional_properties: Dict[str, Any] = {} + __properties: ClassVar[List[str]] = ["email"] + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + * Fields in `self.additional_properties` are added to the output dict. + """ + excluded_fields: Set[str] = set([ + "additional_properties", + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # puts key-value pairs in additional_properties in the top level + if self.additional_properties is not None: + for _key, _value in self.additional_properties.items(): + _dict[_key] = _value + + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of IngressGlobalConfigAllOfLetsencrypt from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "email": obj.get("email") + }) + # store additional fields in additional_properties + for _key in obj.keys(): + if _key not in cls.__properties: + _obj.additional_properties[_key] = obj.get(_key) + + return _obj + + diff --git a/libraries/models/docs/ApplicationHarnessConfig.md b/libraries/models/docs/ApplicationHarnessConfig.md index a589c11a..f10c624f 100644 --- a/libraries/models/docs/ApplicationHarnessConfig.md +++ b/libraries/models/docs/ApplicationHarnessConfig.md @@ -33,6 +33,7 @@ Name | Type | Description | Notes **sentry** | **bool** | | [optional] **proxy** | [**ProxyConf**](ProxyConf.md) | | [optional] **image_name** | **str** | Use this name for the image in place of the default directory name | [optional] +**gateway** | [**GatewayConfig**](GatewayConfig.md) | | [optional] ## Example diff --git a/libraries/models/docs/GatewayConfig.md b/libraries/models/docs/GatewayConfig.md new file mode 100644 index 00000000..c22eea8d --- /dev/null +++ b/libraries/models/docs/GatewayConfig.md @@ -0,0 +1,31 @@ +# GatewayConfig + + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**path_type** | **str** | Ingress path type | +**path** | **str** | Default target path prefix for applications endpoints. To use regular expressions (e.g.'/(pattern)'), also set `route_type` to `ImplementationSpecific`. | + +## Example + +```python +from cloudharness_model.models.gateway_config import GatewayConfig + +# TODO update the JSON string below +json = "{}" +# create an instance of GatewayConfig from a JSON string +gateway_config_instance = GatewayConfig.from_json(json) +# print the JSON string representation of the object +print(GatewayConfig.to_json()) + +# convert the object into a dict +gateway_config_dict = gateway_config_instance.to_dict() +# create an instance of GatewayConfig from a dict +gateway_config_from_dict = GatewayConfig.from_dict(gateway_config_dict) +``` +[[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/libraries/models/docs/HarnessMainConfig.md b/libraries/models/docs/HarnessMainConfig.md index 7940b51c..f9f22fe2 100644 --- a/libraries/models/docs/HarnessMainConfig.md +++ b/libraries/models/docs/HarnessMainConfig.md @@ -20,7 +20,7 @@ Name | Type | Description | Notes **name** | **str** | Base name | [optional] **task_images** | **Dict[str, object]** | | [optional] **build_hash** | **str** | | [optional] -**ingress** | [**IngressConfig**](IngressConfig.md) | | [optional] +**ingress** | [**IngressGlobalConfig**](IngressGlobalConfig.md) | | [optional] ## Example diff --git a/libraries/models/docs/IngressConfig.md b/libraries/models/docs/IngressConfig.md index 5e2add2f..740b04cd 100644 --- a/libraries/models/docs/IngressConfig.md +++ b/libraries/models/docs/IngressConfig.md @@ -6,11 +6,8 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- -**auto** | **bool** | When true, enables automatic template | [optional] -**name** | **str** | | [optional] -**ssl_redirect** | **bool** | | [optional] -**letsencrypt** | [**IngressConfigAllOfLetsencrypt**](IngressConfigAllOfLetsencrypt.md) | | [optional] -**enabled** | **bool** | | [optional] +**path_type** | **str** | Ingress path type | +**path** | **str** | Default target path prefix for applications endpoints. To use regular expressions (e.g.'/(pattern)'), also set `route_type` to `ImplementationSpecific`. | ## Example diff --git a/libraries/models/docs/IngressGlobalConfig.md b/libraries/models/docs/IngressGlobalConfig.md new file mode 100644 index 00000000..02300510 --- /dev/null +++ b/libraries/models/docs/IngressGlobalConfig.md @@ -0,0 +1,36 @@ +# IngressGlobalConfig + + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**auto** | **bool** | When true, enables automatic template | [optional] +**name** | **str** | | [optional] +**path_type** | **str** | Ingress path type | +**path** | **str** | Default target path prefix for applications endpoints. To use regular expressions (e.g.'/(pattern)'), also set `route_type` to `ImplementationSpecific`. | +**ssl_redirect** | **bool** | | [optional] +**letsencrypt** | [**IngressGlobalConfigAllOfLetsencrypt**](IngressGlobalConfigAllOfLetsencrypt.md) | | [optional] +**enabled** | **bool** | | [optional] + +## Example + +```python +from cloudharness_model.models.ingress_global_config import IngressGlobalConfig + +# TODO update the JSON string below +json = "{}" +# create an instance of IngressGlobalConfig from a JSON string +ingress_global_config_instance = IngressGlobalConfig.from_json(json) +# print the JSON string representation of the object +print(IngressGlobalConfig.to_json()) + +# convert the object into a dict +ingress_global_config_dict = ingress_global_config_instance.to_dict() +# create an instance of IngressGlobalConfig from a dict +ingress_global_config_from_dict = IngressGlobalConfig.from_dict(ingress_global_config_dict) +``` +[[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/libraries/models/docs/IngressGlobalConfigAllOfLetsencrypt.md b/libraries/models/docs/IngressGlobalConfigAllOfLetsencrypt.md new file mode 100644 index 00000000..f376f137 --- /dev/null +++ b/libraries/models/docs/IngressGlobalConfigAllOfLetsencrypt.md @@ -0,0 +1,30 @@ +# IngressGlobalConfigAllOfLetsencrypt + + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**email** | **str** | | [optional] + +## Example + +```python +from cloudharness_model.models.ingress_global_config_all_of_letsencrypt import IngressGlobalConfigAllOfLetsencrypt + +# TODO update the JSON string below +json = "{}" +# create an instance of IngressGlobalConfigAllOfLetsencrypt from a JSON string +ingress_global_config_all_of_letsencrypt_instance = IngressGlobalConfigAllOfLetsencrypt.from_json(json) +# print the JSON string representation of the object +print(IngressGlobalConfigAllOfLetsencrypt.to_json()) + +# convert the object into a dict +ingress_global_config_all_of_letsencrypt_dict = ingress_global_config_all_of_letsencrypt_instance.to_dict() +# create an instance of IngressGlobalConfigAllOfLetsencrypt from a dict +ingress_global_config_all_of_letsencrypt_from_dict = IngressGlobalConfigAllOfLetsencrypt.from_dict(ingress_global_config_all_of_letsencrypt_dict) +``` +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + From 3a707ccdfe51f7146639e2de251d89b78a5478a9 Mon Sep 17 00:00:00 2001 From: Filippo Ledda Date: Mon, 9 Mar 2026 16:52:43 +0100 Subject: [PATCH 12/33] CH-224 Use secret for gateway encryption code --- .../helm/templates/auto-gatekeepers.yaml | 24 ++++- deployment-configuration/helm/values.yaml | 1 + deployment-configuration/value-template.yaml | 3 +- docs/model/GatekeeperConf.md | 2 + docs/model/GatewayConfig.md | 4 +- ...GlobalConfig.md => GatewayGlobalConfig.md} | 22 ++--- ...=> GatewayGlobalConfigAllOfLetsencrypt.md} | 16 +-- docs/model/HarnessMainConfig.md | 2 +- libraries/models/README.md | 4 +- libraries/models/api/openapi.yaml | 52 +++++----- .../cloudharness_model/models/__init__.py | 4 +- .../models/gatekeeper_conf.py | 12 ++- .../models/gateway_config.py | 4 +- .../models/gateway_global_config.py | 99 +++++++++++++++++++ ...ateway_global_config_all_of_letsencrypt.py | 83 ++++++++++++++++ .../models/harness_main_config.py | 6 +- libraries/models/docs/GatekeeperConf.md | 2 + libraries/models/docs/GatewayConfig.md | 4 +- libraries/models/docs/GatewayGlobalConfig.md | 36 +++++++ .../GatewayGlobalConfigAllOfLetsencrypt.md | 30 ++++++ libraries/models/docs/HarnessMainConfig.md | 2 +- 21 files changed, 349 insertions(+), 63 deletions(-) rename docs/model/{IngressGlobalConfig.md => GatewayGlobalConfig.md} (55%) rename docs/model/{IngressGlobalConfigAllOfLetsencrypt.md => GatewayGlobalConfigAllOfLetsencrypt.md} (50%) create mode 100644 libraries/models/cloudharness_model/models/gateway_global_config.py create mode 100644 libraries/models/cloudharness_model/models/gateway_global_config_all_of_letsencrypt.py create mode 100644 libraries/models/docs/GatewayGlobalConfig.md create mode 100644 libraries/models/docs/GatewayGlobalConfigAllOfLetsencrypt.md diff --git a/deployment-configuration/helm/templates/auto-gatekeepers.yaml b/deployment-configuration/helm/templates/auto-gatekeepers.yaml index 8a195f7e..d9683e18 100644 --- a/deployment-configuration/helm/templates/auto-gatekeepers.yaml +++ b/deployment-configuration/helm/templates/auto-gatekeepers.yaml @@ -28,7 +28,6 @@ data: enable-default-deny: {{ $noWildcards }} listen: 0.0.0.0:8080 enable-encrypted-token: false - encryption-key: {{ .app.harness.secrets.gatekeeper | default (randAlphaNum 20) | quote }} enable-refresh-tokens: true 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 @@ -101,6 +100,24 @@ data: --- +{{- $gkSecretName := printf "%s-gk" .subdomain }} +{{- $existingGkSecret := (lookup "v1" "Secret" .root.Values.namespace $gkSecretName) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ $gkSecretName }} + namespace: {{ .root.Values.namespace }} + labels: + app: {{ $gkSecretName }} +type: Opaque +stringData: + updated: {{ now | quote }} + {{- if .root.Values.proxy.gatekeeper.secret }} + encryption-key: {{ .root.Values.proxy.gatekeeper.secret | quote }} + {{- else if not (and $existingGkSecret (hasKey $existingGkSecret.data "encryption-key")) }} + encryption-key: {{ randAlphaNum 20 | quote }} + {{- end }} +--- apiVersion: v1 kind: Service metadata: @@ -148,6 +165,11 @@ spec: value: /opt/proxy.yml - name: PROXY_ENABLE_METRICS value: "true" + - name: PROXY_ENCRYPTION_KEY + valueFrom: + secretKeyRef: + name: "{{ .subdomain }}-gk" + key: encryption-key volumeMounts: - name: "{{ .subdomain }}-gk-proxy-config" mountPath: /opt/proxy.yml diff --git a/deployment-configuration/helm/values.yaml b/deployment-configuration/helm/values.yaml index 9d3cf516..c7dd85ae 100644 --- a/deployment-configuration/helm/values.yaml +++ b/deployment-configuration/helm/values.yaml @@ -93,6 +93,7 @@ proxy: image: "quay.io/gogatekeeper/gatekeeper:2.14.3" # -- Default number of gatekeeper replicas replicas: 1 + secret: "" resources: requests: memory: "32Mi" diff --git a/deployment-configuration/value-template.yaml b/deployment-configuration/value-template.yaml index 6d273d9d..fdf11655 100644 --- a/deployment-configuration/value-template.yaml +++ b/deployment-configuration/value-template.yaml @@ -55,8 +55,7 @@ harness: # -- Service port. port: 80 # -- Auto generated secrets key-value pairs. If no value is provided, a random hash is generated - secrets: - gatekeeper: + secrets: {} # -- Specify which services this application uses in the frontend to create proxy ingresses. e.g. - name: mnp-checkout use_services: [] # -- enabled sentry for automated error report diff --git a/docs/model/GatekeeperConf.md b/docs/model/GatekeeperConf.md index e27090fb..268f65b3 100644 --- a/docs/model/GatekeeperConf.md +++ b/docs/model/GatekeeperConf.md @@ -8,6 +8,8 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **image** | **str** | | [optional] **replicas** | **int** | | [optional] +**resources** | [**DeploymentResourcesConf**](DeploymentResourcesConf.md) | | [optional] +**secret** | **str** | | [optional] ## Example diff --git a/docs/model/GatewayConfig.md b/docs/model/GatewayConfig.md index c22eea8d..89542402 100644 --- a/docs/model/GatewayConfig.md +++ b/docs/model/GatewayConfig.md @@ -6,8 +6,8 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- -**path_type** | **str** | Ingress path type | -**path** | **str** | Default target path prefix for applications endpoints. To use regular expressions (e.g.'/(pattern)'), also set `route_type` to `ImplementationSpecific`. | +**path_type** | **str** | Ingress path type | [optional] +**path** | **str** | Default target path prefix for applications endpoints. To use regular expressions (e.g.'/(pattern)'), also set `route_type` to `ImplementationSpecific`. | [optional] ## Example diff --git a/docs/model/IngressGlobalConfig.md b/docs/model/GatewayGlobalConfig.md similarity index 55% rename from docs/model/IngressGlobalConfig.md rename to docs/model/GatewayGlobalConfig.md index 02300510..49e12825 100644 --- a/docs/model/IngressGlobalConfig.md +++ b/docs/model/GatewayGlobalConfig.md @@ -1,4 +1,4 @@ -# IngressGlobalConfig +# GatewayGlobalConfig @@ -8,28 +8,28 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **auto** | **bool** | When true, enables automatic template | [optional] **name** | **str** | | [optional] -**path_type** | **str** | Ingress path type | -**path** | **str** | Default target path prefix for applications endpoints. To use regular expressions (e.g.'/(pattern)'), also set `route_type` to `ImplementationSpecific`. | +**path_type** | **str** | Ingress path type | [optional] +**path** | **str** | Default target path prefix for applications endpoints. To use regular expressions (e.g.'/(pattern)'), also set `route_type` to `ImplementationSpecific`. | [optional] **ssl_redirect** | **bool** | | [optional] -**letsencrypt** | [**IngressGlobalConfigAllOfLetsencrypt**](IngressGlobalConfigAllOfLetsencrypt.md) | | [optional] +**letsencrypt** | [**GatewayGlobalConfigAllOfLetsencrypt**](GatewayGlobalConfigAllOfLetsencrypt.md) | | [optional] **enabled** | **bool** | | [optional] ## Example ```python -from cloudharness_model.models.ingress_global_config import IngressGlobalConfig +from cloudharness_model.models.gateway_global_config import GatewayGlobalConfig # TODO update the JSON string below json = "{}" -# create an instance of IngressGlobalConfig from a JSON string -ingress_global_config_instance = IngressGlobalConfig.from_json(json) +# create an instance of GatewayGlobalConfig from a JSON string +gateway_global_config_instance = GatewayGlobalConfig.from_json(json) # print the JSON string representation of the object -print(IngressGlobalConfig.to_json()) +print(GatewayGlobalConfig.to_json()) # convert the object into a dict -ingress_global_config_dict = ingress_global_config_instance.to_dict() -# create an instance of IngressGlobalConfig from a dict -ingress_global_config_from_dict = IngressGlobalConfig.from_dict(ingress_global_config_dict) +gateway_global_config_dict = gateway_global_config_instance.to_dict() +# create an instance of GatewayGlobalConfig from a dict +gateway_global_config_from_dict = GatewayGlobalConfig.from_dict(gateway_global_config_dict) ``` [[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/IngressGlobalConfigAllOfLetsencrypt.md b/docs/model/GatewayGlobalConfigAllOfLetsencrypt.md similarity index 50% rename from docs/model/IngressGlobalConfigAllOfLetsencrypt.md rename to docs/model/GatewayGlobalConfigAllOfLetsencrypt.md index f376f137..73c79632 100644 --- a/docs/model/IngressGlobalConfigAllOfLetsencrypt.md +++ b/docs/model/GatewayGlobalConfigAllOfLetsencrypt.md @@ -1,4 +1,4 @@ -# IngressGlobalConfigAllOfLetsencrypt +# GatewayGlobalConfigAllOfLetsencrypt @@ -11,19 +11,19 @@ Name | Type | Description | Notes ## Example ```python -from cloudharness_model.models.ingress_global_config_all_of_letsencrypt import IngressGlobalConfigAllOfLetsencrypt +from cloudharness_model.models.gateway_global_config_all_of_letsencrypt import GatewayGlobalConfigAllOfLetsencrypt # TODO update the JSON string below json = "{}" -# create an instance of IngressGlobalConfigAllOfLetsencrypt from a JSON string -ingress_global_config_all_of_letsencrypt_instance = IngressGlobalConfigAllOfLetsencrypt.from_json(json) +# create an instance of GatewayGlobalConfigAllOfLetsencrypt from a JSON string +gateway_global_config_all_of_letsencrypt_instance = GatewayGlobalConfigAllOfLetsencrypt.from_json(json) # print the JSON string representation of the object -print(IngressGlobalConfigAllOfLetsencrypt.to_json()) +print(GatewayGlobalConfigAllOfLetsencrypt.to_json()) # convert the object into a dict -ingress_global_config_all_of_letsencrypt_dict = ingress_global_config_all_of_letsencrypt_instance.to_dict() -# create an instance of IngressGlobalConfigAllOfLetsencrypt from a dict -ingress_global_config_all_of_letsencrypt_from_dict = IngressGlobalConfigAllOfLetsencrypt.from_dict(ingress_global_config_all_of_letsencrypt_dict) +gateway_global_config_all_of_letsencrypt_dict = gateway_global_config_all_of_letsencrypt_instance.to_dict() +# create an instance of GatewayGlobalConfigAllOfLetsencrypt from a dict +gateway_global_config_all_of_letsencrypt_from_dict = GatewayGlobalConfigAllOfLetsencrypt.from_dict(gateway_global_config_all_of_letsencrypt_dict) ``` [[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/HarnessMainConfig.md b/docs/model/HarnessMainConfig.md index f9f22fe2..6b217efb 100644 --- a/docs/model/HarnessMainConfig.md +++ b/docs/model/HarnessMainConfig.md @@ -20,7 +20,7 @@ Name | Type | Description | Notes **name** | **str** | Base name | [optional] **task_images** | **Dict[str, object]** | | [optional] **build_hash** | **str** | | [optional] -**ingress** | [**IngressGlobalConfig**](IngressGlobalConfig.md) | | [optional] +**ingress** | [**GatewayGlobalConfig**](GatewayGlobalConfig.md) | | [optional] ## Example diff --git a/libraries/models/README.md b/libraries/models/README.md index 466eb6ab..9b23b23d 100644 --- a/libraries/models/README.md +++ b/libraries/models/README.md @@ -89,10 +89,10 @@ Class | Method | HTTP request | Description - [FileResourcesConfig](docs/FileResourcesConfig.md) - [GatekeeperConf](docs/GatekeeperConf.md) - [GatewayConfig](docs/GatewayConfig.md) + - [GatewayGlobalConfig](docs/GatewayGlobalConfig.md) + - [GatewayGlobalConfigAllOfLetsencrypt](docs/GatewayGlobalConfigAllOfLetsencrypt.md) - [GitDependencyConfig](docs/GitDependencyConfig.md) - [HarnessMainConfig](docs/HarnessMainConfig.md) - - [IngressGlobalConfig](docs/IngressGlobalConfig.md) - - [IngressGlobalConfigAllOfLetsencrypt](docs/IngressGlobalConfigAllOfLetsencrypt.md) - [JupyterHubConfig](docs/JupyterHubConfig.md) - [NameValue](docs/NameValue.md) - [NamedObject](docs/NamedObject.md) diff --git a/libraries/models/api/openapi.yaml b/libraries/models/api/openapi.yaml index fb02410b..79efe2dc 100644 --- a/libraries/models/api/openapi.yaml +++ b/libraries/models/api/openapi.yaml @@ -588,7 +588,7 @@ components: description: '' type: string ingress: - $ref: '#/components/schemas/IngressGlobalConfig' + $ref: '#/components/schemas/GatewayGlobalConfig' description: '' additionalProperties: true Organization: @@ -887,6 +887,12 @@ components: replicas: format: int32 type: integer + resources: + $ref: '#/components/schemas/DeploymentResourcesConf' + description: '' + secret: + description: '' + type: string example: image: 'quay.io/gogatekeeper/gatekeeper:2.14.3' replicas: 5 @@ -1018,7 +1024,27 @@ components: $ref: '#/components/schemas/GatewayConfig' description: '' additionalProperties: true - IngressGlobalConfig: + GatewayConfig: + description: '' + type: object + allOf: + - + required: [] + type: object + properties: + pathType: + description: 'Ingress path type ' + type: string + example: '"Prefix"' + path: + description: | + Default target path prefix for applications endpoints. + To use regular expressions (e.g.'/(pattern)'), also set `route_type` to + `ImplementationSpecific`. + type: string + example: '"/"' + additionalProperties: true + GatewayGlobalConfig: description: '' type: object allOf: @@ -1042,25 +1068,3 @@ components: - $ref: '#/components/schemas/GatewayConfig' additionalProperties: true - GatewayConfig: - description: '' - type: object - allOf: - - - required: - - path - - pathType - type: object - properties: - pathType: - description: 'Ingress path type ' - type: string - example: '"Prefix"' - path: - description: | - Default target path prefix for applications endpoints. - To use regular expressions (e.g.'/(pattern)'), also set `route_type` to - `ImplementationSpecific`. - type: string - example: '"/"' - additionalProperties: true diff --git a/libraries/models/cloudharness_model/models/__init__.py b/libraries/models/cloudharness_model/models/__init__.py index cf529ebb..fb755e9f 100644 --- a/libraries/models/cloudharness_model/models/__init__.py +++ b/libraries/models/cloudharness_model/models/__init__.py @@ -36,10 +36,10 @@ from cloudharness_model.models.file_resources_config import FileResourcesConfig from cloudharness_model.models.gatekeeper_conf import GatekeeperConf from cloudharness_model.models.gateway_config import GatewayConfig +from cloudharness_model.models.gateway_global_config import GatewayGlobalConfig +from cloudharness_model.models.gateway_global_config_all_of_letsencrypt import GatewayGlobalConfigAllOfLetsencrypt from cloudharness_model.models.git_dependency_config import GitDependencyConfig from cloudharness_model.models.harness_main_config import HarnessMainConfig -from cloudharness_model.models.ingress_global_config import IngressGlobalConfig -from cloudharness_model.models.ingress_global_config_all_of_letsencrypt import IngressGlobalConfigAllOfLetsencrypt from cloudharness_model.models.jupyter_hub_config import JupyterHubConfig from cloudharness_model.models.name_value import NameValue from cloudharness_model.models.named_object import NamedObject diff --git a/libraries/models/cloudharness_model/models/gatekeeper_conf.py b/libraries/models/cloudharness_model/models/gatekeeper_conf.py index 2e757429..e83212f5 100644 --- a/libraries/models/cloudharness_model/models/gatekeeper_conf.py +++ b/libraries/models/cloudharness_model/models/gatekeeper_conf.py @@ -25,6 +25,7 @@ from pydantic import BaseModel, Field, field_validator, StrictStr, StrictBool, StrictInt, StrictFloat from typing import ClassVar, List, Dict, Any, Union, Optional, Annotated import importlib +from cloudharness_model.models.deployment_resources_conf import DeploymentResourcesConf class GatekeeperConf(CloudHarnessBaseModel): """ @@ -32,8 +33,10 @@ class GatekeeperConf(CloudHarnessBaseModel): """ # noqa: E501 image: Optional[StrictStr] = None replicas: Optional[StrictInt] = None + resources: Optional[DeploymentResourcesConf] = None + secret: Optional[StrictStr] = None additional_properties: Dict[str, Any] = {} - __properties: ClassVar[List[str]] = ["image", "replicas"] + __properties: ClassVar[List[str]] = ["image", "replicas", "resources", "secret"] def to_dict(self) -> Dict[str, Any]: """Return the dictionary representation of the model using alias. @@ -55,6 +58,9 @@ def to_dict(self) -> Dict[str, Any]: exclude=excluded_fields, exclude_none=True, ) + # override the default output from pydantic by calling `to_dict()` of resources + if self.resources: + _dict['resources'] = self.resources.to_dict() # puts key-value pairs in additional_properties in the top level if self.additional_properties is not None: for _key, _value in self.additional_properties.items(): @@ -73,7 +79,9 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: _obj = cls.model_validate({ "image": obj.get("image"), - "replicas": obj.get("replicas") + "replicas": obj.get("replicas"), + "resources": DeploymentResourcesConf.from_dict(obj["resources"]) if obj.get("resources") is not None else None, + "secret": obj.get("secret") }) # store additional fields in additional_properties for _key in obj.keys(): diff --git a/libraries/models/cloudharness_model/models/gateway_config.py b/libraries/models/cloudharness_model/models/gateway_config.py index 634fa400..9e195cbf 100644 --- a/libraries/models/cloudharness_model/models/gateway_config.py +++ b/libraries/models/cloudharness_model/models/gateway_config.py @@ -30,8 +30,8 @@ class GatewayConfig(CloudHarnessBaseModel): """ """ # noqa: E501 - path_type: StrictStr = Field(description="Ingress path type ", alias="pathType") - path: StrictStr = Field(description="Default target path prefix for applications endpoints. To use regular expressions (e.g.'/(pattern)'), also set `route_type` to `ImplementationSpecific`. ") + path_type: Optional[StrictStr] = Field(default=None, description="Ingress path type ", alias="pathType") + path: Optional[StrictStr] = Field(default=None, description="Default target path prefix for applications endpoints. To use regular expressions (e.g.'/(pattern)'), also set `route_type` to `ImplementationSpecific`. ") additional_properties: Dict[str, Any] = {} __properties: ClassVar[List[str]] = ["pathType", "path"] diff --git a/libraries/models/cloudharness_model/models/gateway_global_config.py b/libraries/models/cloudharness_model/models/gateway_global_config.py new file mode 100644 index 00000000..12553344 --- /dev/null +++ b/libraries/models/cloudharness_model/models/gateway_global_config.py @@ -0,0 +1,99 @@ +# coding: utf-8 + +""" + cloudharness + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 1.0.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from typing import Optional, Set +from typing_extensions import Self + + +from cloudharness_model.base_model import CloudHarnessBaseModel +from pydantic import BaseModel, Field, field_validator, StrictStr, StrictBool, StrictInt, StrictFloat +from typing import ClassVar, List, Dict, Any, Union, Optional, Annotated +import importlib +from cloudharness_model.models.gateway_global_config_all_of_letsencrypt import GatewayGlobalConfigAllOfLetsencrypt + +class GatewayGlobalConfig(CloudHarnessBaseModel): + """ + + """ # noqa: E501 + auto: Optional[StrictBool] = Field(default=None, description="When true, enables automatic template") + name: Optional[StrictStr] = None + path_type: Optional[StrictStr] = Field(default=None, description="Ingress path type ", alias="pathType") + path: Optional[StrictStr] = Field(default=None, description="Default target path prefix for applications endpoints. To use regular expressions (e.g.'/(pattern)'), also set `route_type` to `ImplementationSpecific`. ") + ssl_redirect: Optional[StrictBool] = None + letsencrypt: Optional[GatewayGlobalConfigAllOfLetsencrypt] = None + enabled: Optional[StrictBool] = None + additional_properties: Dict[str, Any] = {} + __properties: ClassVar[List[str]] = ["auto", "name", "pathType", "path", "ssl_redirect", "letsencrypt", "enabled"] + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + * Fields in `self.additional_properties` are added to the output dict. + """ + excluded_fields: Set[str] = set([ + "additional_properties", + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of letsencrypt + if self.letsencrypt: + _dict['letsencrypt'] = self.letsencrypt.to_dict() + # puts key-value pairs in additional_properties in the top level + if self.additional_properties is not None: + for _key, _value in self.additional_properties.items(): + _dict[_key] = _value + + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of GatewayGlobalConfig from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "auto": obj.get("auto"), + "name": obj.get("name"), + "pathType": obj.get("pathType"), + "path": obj.get("path"), + "ssl_redirect": obj.get("ssl_redirect"), + "letsencrypt": GatewayGlobalConfigAllOfLetsencrypt.from_dict(obj["letsencrypt"]) if obj.get("letsencrypt") is not None else None, + "enabled": obj.get("enabled") + }) + # store additional fields in additional_properties + for _key in obj.keys(): + if _key not in cls.__properties: + _obj.additional_properties[_key] = obj.get(_key) + + return _obj + + diff --git a/libraries/models/cloudharness_model/models/gateway_global_config_all_of_letsencrypt.py b/libraries/models/cloudharness_model/models/gateway_global_config_all_of_letsencrypt.py new file mode 100644 index 00000000..f402310d --- /dev/null +++ b/libraries/models/cloudharness_model/models/gateway_global_config_all_of_letsencrypt.py @@ -0,0 +1,83 @@ +# coding: utf-8 + +""" + cloudharness + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 1.0.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from typing import Optional, Set +from typing_extensions import Self + + +from cloudharness_model.base_model import CloudHarnessBaseModel +from pydantic import BaseModel, Field, field_validator, StrictStr, StrictBool, StrictInt, StrictFloat +from typing import ClassVar, List, Dict, Any, Union, Optional, Annotated +import importlib + +class GatewayGlobalConfigAllOfLetsencrypt(CloudHarnessBaseModel): + """ + + """ # noqa: E501 + email: Optional[StrictStr] = None + additional_properties: Dict[str, Any] = {} + __properties: ClassVar[List[str]] = ["email"] + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + * Fields in `self.additional_properties` are added to the output dict. + """ + excluded_fields: Set[str] = set([ + "additional_properties", + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # puts key-value pairs in additional_properties in the top level + if self.additional_properties is not None: + for _key, _value in self.additional_properties.items(): + _dict[_key] = _value + + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of GatewayGlobalConfigAllOfLetsencrypt from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "email": obj.get("email") + }) + # store additional fields in additional_properties + for _key in obj.keys(): + if _key not in cls.__properties: + _obj.additional_properties[_key] = obj.get(_key) + + return _obj + + diff --git a/libraries/models/cloudharness_model/models/harness_main_config.py b/libraries/models/cloudharness_model/models/harness_main_config.py index 380e3ce6..ba25128f 100644 --- a/libraries/models/cloudharness_model/models/harness_main_config.py +++ b/libraries/models/cloudharness_model/models/harness_main_config.py @@ -27,7 +27,7 @@ import importlib from cloudharness_model.models.application_config import ApplicationConfig from cloudharness_model.models.backup_config import BackupConfig -from cloudharness_model.models.ingress_global_config import IngressGlobalConfig +from cloudharness_model.models.gateway_global_config import GatewayGlobalConfig from cloudharness_model.models.name_value import NameValue from cloudharness_model.models.registry_config import RegistryConfig @@ -49,7 +49,7 @@ class HarnessMainConfig(CloudHarnessBaseModel): name: Optional[StrictStr] = Field(default=None, description="Base name") task_images: Optional[Dict[str, Any]] = Field(default=None, alias="task-images") build_hash: Optional[StrictStr] = None - ingress: Optional[IngressGlobalConfig] = None + ingress: Optional[GatewayGlobalConfig] = None additional_properties: Dict[str, Any] = {} __properties: ClassVar[List[str]] = ["local", "secured_gatekeepers", "domain", "namespace", "mainapp", "registry", "tag", "apps", "env", "privenv", "backup", "name", "task-images", "build_hash", "ingress"] @@ -139,7 +139,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "name": obj.get("name"), "task-images": obj.get("task-images"), "build_hash": obj.get("build_hash"), - "ingress": IngressGlobalConfig.from_dict(obj["ingress"]) if obj.get("ingress") is not None else None + "ingress": GatewayGlobalConfig.from_dict(obj["ingress"]) if obj.get("ingress") is not None else None }) # store additional fields in additional_properties for _key in obj.keys(): diff --git a/libraries/models/docs/GatekeeperConf.md b/libraries/models/docs/GatekeeperConf.md index e27090fb..268f65b3 100644 --- a/libraries/models/docs/GatekeeperConf.md +++ b/libraries/models/docs/GatekeeperConf.md @@ -8,6 +8,8 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **image** | **str** | | [optional] **replicas** | **int** | | [optional] +**resources** | [**DeploymentResourcesConf**](DeploymentResourcesConf.md) | | [optional] +**secret** | **str** | | [optional] ## Example diff --git a/libraries/models/docs/GatewayConfig.md b/libraries/models/docs/GatewayConfig.md index c22eea8d..89542402 100644 --- a/libraries/models/docs/GatewayConfig.md +++ b/libraries/models/docs/GatewayConfig.md @@ -6,8 +6,8 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- -**path_type** | **str** | Ingress path type | -**path** | **str** | Default target path prefix for applications endpoints. To use regular expressions (e.g.'/(pattern)'), also set `route_type` to `ImplementationSpecific`. | +**path_type** | **str** | Ingress path type | [optional] +**path** | **str** | Default target path prefix for applications endpoints. To use regular expressions (e.g.'/(pattern)'), also set `route_type` to `ImplementationSpecific`. | [optional] ## Example diff --git a/libraries/models/docs/GatewayGlobalConfig.md b/libraries/models/docs/GatewayGlobalConfig.md new file mode 100644 index 00000000..49e12825 --- /dev/null +++ b/libraries/models/docs/GatewayGlobalConfig.md @@ -0,0 +1,36 @@ +# GatewayGlobalConfig + + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**auto** | **bool** | When true, enables automatic template | [optional] +**name** | **str** | | [optional] +**path_type** | **str** | Ingress path type | [optional] +**path** | **str** | Default target path prefix for applications endpoints. To use regular expressions (e.g.'/(pattern)'), also set `route_type` to `ImplementationSpecific`. | [optional] +**ssl_redirect** | **bool** | | [optional] +**letsencrypt** | [**GatewayGlobalConfigAllOfLetsencrypt**](GatewayGlobalConfigAllOfLetsencrypt.md) | | [optional] +**enabled** | **bool** | | [optional] + +## Example + +```python +from cloudharness_model.models.gateway_global_config import GatewayGlobalConfig + +# TODO update the JSON string below +json = "{}" +# create an instance of GatewayGlobalConfig from a JSON string +gateway_global_config_instance = GatewayGlobalConfig.from_json(json) +# print the JSON string representation of the object +print(GatewayGlobalConfig.to_json()) + +# convert the object into a dict +gateway_global_config_dict = gateway_global_config_instance.to_dict() +# create an instance of GatewayGlobalConfig from a dict +gateway_global_config_from_dict = GatewayGlobalConfig.from_dict(gateway_global_config_dict) +``` +[[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/libraries/models/docs/GatewayGlobalConfigAllOfLetsencrypt.md b/libraries/models/docs/GatewayGlobalConfigAllOfLetsencrypt.md new file mode 100644 index 00000000..73c79632 --- /dev/null +++ b/libraries/models/docs/GatewayGlobalConfigAllOfLetsencrypt.md @@ -0,0 +1,30 @@ +# GatewayGlobalConfigAllOfLetsencrypt + + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**email** | **str** | | [optional] + +## Example + +```python +from cloudharness_model.models.gateway_global_config_all_of_letsencrypt import GatewayGlobalConfigAllOfLetsencrypt + +# TODO update the JSON string below +json = "{}" +# create an instance of GatewayGlobalConfigAllOfLetsencrypt from a JSON string +gateway_global_config_all_of_letsencrypt_instance = GatewayGlobalConfigAllOfLetsencrypt.from_json(json) +# print the JSON string representation of the object +print(GatewayGlobalConfigAllOfLetsencrypt.to_json()) + +# convert the object into a dict +gateway_global_config_all_of_letsencrypt_dict = gateway_global_config_all_of_letsencrypt_instance.to_dict() +# create an instance of GatewayGlobalConfigAllOfLetsencrypt from a dict +gateway_global_config_all_of_letsencrypt_from_dict = GatewayGlobalConfigAllOfLetsencrypt.from_dict(gateway_global_config_all_of_letsencrypt_dict) +``` +[[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/libraries/models/docs/HarnessMainConfig.md b/libraries/models/docs/HarnessMainConfig.md index f9f22fe2..6b217efb 100644 --- a/libraries/models/docs/HarnessMainConfig.md +++ b/libraries/models/docs/HarnessMainConfig.md @@ -20,7 +20,7 @@ Name | Type | Description | Notes **name** | **str** | Base name | [optional] **task_images** | **Dict[str, object]** | | [optional] **build_hash** | **str** | | [optional] -**ingress** | [**IngressGlobalConfig**](IngressGlobalConfig.md) | | [optional] +**ingress** | [**GatewayGlobalConfig**](GatewayGlobalConfig.md) | | [optional] ## Example From 1bccfff77d47f3fd270b98f97a476033b6bcaa2e Mon Sep 17 00:00:00 2001 From: Filippo Ledda Date: Mon, 9 Mar 2026 17:07:24 +0100 Subject: [PATCH 13/33] CH-243 path generation fix --- deployment-configuration/helm/templates/ingress.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deployment-configuration/helm/templates/ingress.yaml b/deployment-configuration/helm/templates/ingress.yaml index 1db0c2df..910f0886 100644 --- a/deployment-configuration/helm/templates/ingress.yaml +++ b/deployment-configuration/helm/templates/ingress.yaml @@ -21,7 +21,7 @@ {{- else if hasSuffix "/*" $uri }} {{- $cleanPath := trimSuffix "/*" $uri }} {{- $pathWithoutSlash := trimPrefix "/" $cleanPath }} - - path: {{ printf "/%s/" $pathWithoutSlash }} + - path: {{ printf "%s%s" $app.harness.gateway.path $pathWithoutSlash }} pathType: Prefix backend: service: @@ -30,8 +30,8 @@ number: {{ $app.harness.service.port | default 80 }} {{- else if (not (contains "*" $uri)) }} {{- $pathWithoutSlash := trimPrefix "/" $uri }} - - path: {{ printf "%s/%s" $app.harness.gateway.path $pathWithoutSlash }} - pathType: Prefix + - path: {{ printf "%s%s" $app.harness.gateway.path $pathWithoutSlash }} + pathType: Exact backend: service: name: {{ $app.harness.service.name | quote }} From ad29c79087121639cdf303ca6b64557b62f61e38 Mon Sep 17 00:00:00 2001 From: Filippo Ledda Date: Mon, 9 Mar 2026 17:30:09 +0100 Subject: [PATCH 14/33] CH-224 GK helm template fix --- deployment-configuration/helm/templates/auto-gatekeepers.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment-configuration/helm/templates/auto-gatekeepers.yaml b/deployment-configuration/helm/templates/auto-gatekeepers.yaml index d9683e18..948908c9 100644 --- a/deployment-configuration/helm/templates/auto-gatekeepers.yaml +++ b/deployment-configuration/helm/templates/auto-gatekeepers.yaml @@ -114,7 +114,7 @@ stringData: updated: {{ now | quote }} {{- if .root.Values.proxy.gatekeeper.secret }} encryption-key: {{ .root.Values.proxy.gatekeeper.secret | quote }} - {{- else if not (and $existingGkSecret (hasKey $existingGkSecret.data "encryption-key")) }} + {{- else if not (and (eq (typeOf $existingGkSecret.data) (typeOf dict)) (hasKey $existingGkSecret.data "encryption-key")) }} encryption-key: {{ randAlphaNum 20 | quote }} {{- end }} --- From 9360472841d2faa763fe368346a30a85ee85f88a Mon Sep 17 00:00:00 2001 From: Filippo Ledda Date: Mon, 9 Mar 2026 18:09:07 +0100 Subject: [PATCH 15/33] CH-224 GK helm template fix --- .../helm/templates/auto-gatekeepers.yaml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/deployment-configuration/helm/templates/auto-gatekeepers.yaml b/deployment-configuration/helm/templates/auto-gatekeepers.yaml index 948908c9..ce075f02 100644 --- a/deployment-configuration/helm/templates/auto-gatekeepers.yaml +++ b/deployment-configuration/helm/templates/auto-gatekeepers.yaml @@ -114,8 +114,18 @@ stringData: updated: {{ now | quote }} {{- if .root.Values.proxy.gatekeeper.secret }} encryption-key: {{ .root.Values.proxy.gatekeeper.secret | quote }} - {{- else if not (and (eq (typeOf $existingGkSecret.data) (typeOf dict)) (hasKey $existingGkSecret.data "encryption-key")) }} + {{- else }} + {{- $hasExisting := false }} + {{- if $existingGkSecret }} + {{- if eq (typeOf $existingGkSecret.data) (typeOf dict) }} + {{- if hasKey $existingGkSecret.data "encryption-key" }} + {{- $hasExisting = true }} + {{- end }} + {{- end }} + {{- end }} + {{- if not $hasExisting }} encryption-key: {{ randAlphaNum 20 | quote }} + {{- end }} {{- end }} --- apiVersion: v1 From b08d27197246fa0321f5387b0d8493c11944e912 Mon Sep 17 00:00:00 2001 From: Filippo Ledda Date: Mon, 9 Mar 2026 18:38:34 +0100 Subject: [PATCH 16/33] CH-243 post refactoring issue with certificates fix --- .../helm/templates/ingress.yaml | 4 ++-- .../helm/templates/tls-secret.yaml | 14 +++++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/deployment-configuration/helm/templates/ingress.yaml b/deployment-configuration/helm/templates/ingress.yaml index 910f0886..f79b2d61 100644 --- a/deployment-configuration/helm/templates/ingress.yaml +++ b/deployment-configuration/helm/templates/ingress.yaml @@ -150,7 +150,7 @@ spec: - {{ $domain | quote }} {{- end }} {{- end }} - secretName: tls-secret + secretName: {{ printf "tls-secret-%s" $appIngressName }} {{- end }} {{- if $app.harness.use_services }} --- @@ -209,7 +209,7 @@ spec: - {{ $domain | quote }} {{- end }} {{- end }} - secretName: tls-secret + secretName: {{ printf "tls-secret-%s" $appIngressName }} {{- end }} {{- end }} {{- end }} diff --git a/deployment-configuration/helm/templates/tls-secret.yaml b/deployment-configuration/helm/templates/tls-secret.yaml index ac871d2f..2979c988 100644 --- a/deployment-configuration/helm/templates/tls-secret.yaml +++ b/deployment-configuration/helm/templates/tls-secret.yaml @@ -1,13 +1,17 @@ -{{ if and .Values.local .Values.tls }} +{{- if and .Values.local .Values.tls }} +{{- range $app := .Values.apps }} + {{- if or $app.harness.subdomain $app.harness.domain $app.harness.aliases }} + {{- $appIngressName := default $app.harness.name $app.harness.service.name }} apiVersion: v1 kind: Secret metadata: - name: tls-secret + name: {{ printf "tls-secret-%s" $appIngressName }} type: kubernetes.io/tls data: - tls.crt: {{ .Files.Get "resources/certs/tls.crt" | b64enc | quote }} - tls.key: {{ .Files.Get "resources/certs/tls.key" | b64enc | quote }} + tls.crt: {{ $.Files.Get "resources/certs/tls.crt" | b64enc | quote }} + tls.key: {{ $.Files.Get "resources/certs/tls.key" | b64enc | quote }} --- + {{- end }} +{{- end }} {{- end }} - From 54b73e9a5877ea8c92ccf1b05cbfe1905f680ab4 Mon Sep 17 00:00:00 2001 From: Filippo Ledda Date: Mon, 9 Mar 2026 18:50:52 +0100 Subject: [PATCH 17/33] CH-248 external database configuration --- .../helm/templates/auto-deployments.yaml | 10 ++ .../helm/templates/auto-secrets.yaml | 19 ++++ .../helm/templates/configmap.yaml | 3 + docs/model/DatabaseConfig.md | 32 +++++++ docs/model/DatabaseDeploymentConfig.md | 1 + docs/model/PortConfig.md | 31 ++++++ .../cloudharness/applications.py | 5 + libraries/models/README.md | 2 + libraries/models/api/openapi.yaml | 39 ++++++++ .../cloudharness_model/models/__init__.py | 2 + .../models/database_config.py | 95 +++++++++++++++++++ .../models/database_deployment_config.py | 6 +- .../cloudharness_model/models/port_config.py | 85 +++++++++++++++++ libraries/models/docs/DatabaseConfig.md | 32 +++++++ .../models/docs/DatabaseDeploymentConfig.md | 1 + libraries/models/docs/PortConfig.md | 31 ++++++ .../ch_cli_tools/codefresh.py | 8 ++ 17 files changed, 400 insertions(+), 2 deletions(-) create mode 100644 docs/model/DatabaseConfig.md create mode 100644 docs/model/PortConfig.md create mode 100644 libraries/models/cloudharness_model/models/database_config.py create mode 100644 libraries/models/cloudharness_model/models/port_config.py create mode 100644 libraries/models/docs/DatabaseConfig.md create mode 100644 libraries/models/docs/PortConfig.md diff --git a/deployment-configuration/helm/templates/auto-deployments.yaml b/deployment-configuration/helm/templates/auto-deployments.yaml index 874ec99d..f3be4850 100644 --- a/deployment-configuration/helm/templates/auto-deployments.yaml +++ b/deployment-configuration/helm/templates/auto-deployments.yaml @@ -140,6 +140,11 @@ spec: mountPath: "/opt/cloudharness/resources/secrets/{{ .app.harness.name }}" readOnly: true {{- end }} + {{- if and .app.harness.database .app.harness.database.external_connect_string }} + - name: db-external + mountPath: "/opt/cloudharness/resources/db" + readOnly: true + {{- end }} volumes: - name: cloudharness-allvalues configMap: @@ -169,6 +174,11 @@ spec: secret: secretName: {{ .app.harness.deployment.name }} {{- end }} + {{- if and .app.harness.database .app.harness.database.external_connect_string }} + - name: db-external + secret: + secretName: {{ printf "%s-db" .app.harness.deployment.name }} + {{- end }} --- {{- end }} diff --git a/deployment-configuration/helm/templates/auto-secrets.yaml b/deployment-configuration/helm/templates/auto-secrets.yaml index 0a1ccc3b..2254391b 100644 --- a/deployment-configuration/helm/templates/auto-secrets.yaml +++ b/deployment-configuration/helm/templates/auto-secrets.yaml @@ -57,4 +57,23 @@ stringData: {{- end }}{{- end }}{{- end }} {{- end }} {{- end }} +{{- end }} +{{- define "deploy_utils.db_external_secret" }} +{{- $secret_name := printf "%s-db" .app.harness.deployment.name }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ $secret_name }} + namespace: {{ .root.Values.namespace }} + labels: + app: {{ .app.harness.deployment.name }} +type: Opaque +stringData: + external_connect_string: {{ .app.harness.database.external_connect_string | quote }} +--- +{{- end }} +{{- range $app := .Values.apps }} + {{- if and $app.harness.database $app.harness.database.external_connect_string }} + {{- include "deploy_utils.db_external_secret" (dict "root" $ "app" $app) }} + {{- end }} {{- end }} \ No newline at end of file diff --git a/deployment-configuration/helm/templates/configmap.yaml b/deployment-configuration/helm/templates/configmap.yaml index ae526553..47076a05 100644 --- a/deployment-configuration/helm/templates/configmap.yaml +++ b/deployment-configuration/helm/templates/configmap.yaml @@ -20,5 +20,8 @@ data: {{- range $key, $val := .Values.apps }} {{- $app := get $values_copy.apps $key }} {{- $tmp := set $app.harness "secrets" dict }} + {{- if $app.harness.database }} + {{- $tmp := unset $app.harness.database "external_connect_string" }} + {{- end }} {{- end }} {{ $values_copy | toYaml | indent 4 }} \ No newline at end of file diff --git a/docs/model/DatabaseConfig.md b/docs/model/DatabaseConfig.md new file mode 100644 index 00000000..c6b3bf21 --- /dev/null +++ b/docs/model/DatabaseConfig.md @@ -0,0 +1,32 @@ +# DatabaseConfig + + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**image** | **str** | | [optional] +**name** | **str** | | [optional] +**ports** | [**List[PortConfig]**](PortConfig.md) | | [optional] + +## Example + +```python +from cloudharness_model.models.database_config import DatabaseConfig + +# TODO update the JSON string below +json = "{}" +# create an instance of DatabaseConfig from a JSON string +database_config_instance = DatabaseConfig.from_json(json) +# print the JSON string representation of the object +print(DatabaseConfig.to_json()) + +# convert the object into a dict +database_config_dict = database_config_instance.to_dict() +# create an instance of DatabaseConfig from a dict +database_config_from_dict = DatabaseConfig.from_dict(database_config_dict) +``` +[[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/DatabaseDeploymentConfig.md b/docs/model/DatabaseDeploymentConfig.md index ae920cd1..c4924aab 100644 --- a/docs/model/DatabaseDeploymentConfig.md +++ b/docs/model/DatabaseDeploymentConfig.md @@ -17,6 +17,7 @@ Name | Type | Description | Notes **postgres** | **Dict[str, object]** | | [optional] **neo4j** | **object** | Neo4j database specific configuration | [optional] **resources** | [**DeploymentResourcesConf**](DeploymentResourcesConf.md) | | [optional] +**external_connect_string** | **str** | Specify if the database is external. If not null, auto deployment if set will not be used. Leave it as an empty string and the connect string will be provided as a secret to be provided at CI/CD (recommended) | [optional] ## Example diff --git a/docs/model/PortConfig.md b/docs/model/PortConfig.md new file mode 100644 index 00000000..726c3c0a --- /dev/null +++ b/docs/model/PortConfig.md @@ -0,0 +1,31 @@ +# PortConfig + + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**name** | **str** | | [optional] +**port** | **int** | | [optional] + +## Example + +```python +from cloudharness_model.models.port_config import PortConfig + +# TODO update the JSON string below +json = "{}" +# create an instance of PortConfig from a JSON string +port_config_instance = PortConfig.from_json(json) +# print the JSON string representation of the object +print(PortConfig.to_json()) + +# convert the object into a dict +port_config_dict = port_config_instance.to_dict() +# create an instance of PortConfig from a dict +port_config_from_dict = PortConfig.from_dict(port_config_dict) +``` +[[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/libraries/cloudharness-common/cloudharness/applications.py b/libraries/cloudharness-common/cloudharness/applications.py index cb73a6fc..86c8867d 100644 --- a/libraries/cloudharness-common/cloudharness/applications.py +++ b/libraries/cloudharness-common/cloudharness/applications.py @@ -1,3 +1,4 @@ +import os from typing import List from cloudharness.utils.config import CloudharnessConfig, ConfigObject @@ -36,6 +37,10 @@ def is_sentry_enabled(self) -> bool: return self.harness.sentry def get_db_connection_string(self, **kwargs) -> str: + external_connect_string_path = "/opt/cloudharness/resources/db/external_connect_string" + if os.path.isfile(external_connect_string_path): + with open(external_connect_string_path) as f: + return f.read().strip() if not self.is_auto_db(): raise ConfigurationCallException( f"Cannot get configuration string: application {self.name} has no database enabled.") diff --git a/libraries/models/README.md b/libraries/models/README.md index 9b23b23d..1555b03a 100644 --- a/libraries/models/README.md +++ b/libraries/models/README.md @@ -80,6 +80,7 @@ Class | Method | HTTP request | Description - [CDCEvent](docs/CDCEvent.md) - [CDCEventMeta](docs/CDCEventMeta.md) - [CpuMemoryConfig](docs/CpuMemoryConfig.md) + - [DatabaseConfig](docs/DatabaseConfig.md) - [DatabaseDeploymentConfig](docs/DatabaseDeploymentConfig.md) - [DeploymentAutoArtifactConfig](docs/DeploymentAutoArtifactConfig.md) - [DeploymentResourcesConf](docs/DeploymentResourcesConf.md) @@ -97,6 +98,7 @@ Class | Method | HTTP request | Description - [NameValue](docs/NameValue.md) - [NamedObject](docs/NamedObject.md) - [Organization](docs/Organization.md) + - [PortConfig](docs/PortConfig.md) - [ProxyConf](docs/ProxyConf.md) - [ProxyPayloadConf](docs/ProxyPayloadConf.md) - [ProxyTimeoutConf](docs/ProxyTimeoutConf.md) diff --git a/libraries/models/api/openapi.yaml b/libraries/models/api/openapi.yaml index 79efe2dc..f3e27735 100644 --- a/libraries/models/api/openapi.yaml +++ b/libraries/models/api/openapi.yaml @@ -654,6 +654,16 @@ components: resources: $ref: '#/components/schemas/DeploymentResourcesConf' description: Database deployment resources + external_connect_string: + description: >- + Specify if the database is external. If not null, auto deployment if set will + not + + be used. Leva it as an empty string and the connect string will be provided + as + + a secret to be provided at CI/CD (recommended) + type: string - $ref: '#/components/schemas/AutoArtifactSpec' additionalProperties: true @@ -1068,3 +1078,32 @@ components: - $ref: '#/components/schemas/GatewayConfig' additionalProperties: true + PortConfig: + title: Root Type for PortConfig + description: '' + type: object + properties: + name: + type: string + port: + format: int32 + type: integer + example: + name: http + port: 8080 + DatabaseConfig: + description: '' + type: object + properties: + image: + description: '' + type: string + name: + description: '' + type: string + ports: + description: '' + type: array + items: + $ref: '#/components/schemas/PortConfig' + additionalProperties: true diff --git a/libraries/models/cloudharness_model/models/__init__.py b/libraries/models/cloudharness_model/models/__init__.py index fb755e9f..f8b3b7ba 100644 --- a/libraries/models/cloudharness_model/models/__init__.py +++ b/libraries/models/cloudharness_model/models/__init__.py @@ -27,6 +27,7 @@ from cloudharness_model.models.cdc_event import CDCEvent from cloudharness_model.models.cdc_event_meta import CDCEventMeta from cloudharness_model.models.cpu_memory_config import CpuMemoryConfig +from cloudharness_model.models.database_config import DatabaseConfig from cloudharness_model.models.database_deployment_config import DatabaseDeploymentConfig from cloudharness_model.models.deployment_auto_artifact_config import DeploymentAutoArtifactConfig from cloudharness_model.models.deployment_resources_conf import DeploymentResourcesConf @@ -44,6 +45,7 @@ from cloudharness_model.models.name_value import NameValue from cloudharness_model.models.named_object import NamedObject from cloudharness_model.models.organization import Organization +from cloudharness_model.models.port_config import PortConfig from cloudharness_model.models.proxy_conf import ProxyConf from cloudharness_model.models.proxy_payload_conf import ProxyPayloadConf from cloudharness_model.models.proxy_timeout_conf import ProxyTimeoutConf diff --git a/libraries/models/cloudharness_model/models/database_config.py b/libraries/models/cloudharness_model/models/database_config.py new file mode 100644 index 00000000..a2a914c5 --- /dev/null +++ b/libraries/models/cloudharness_model/models/database_config.py @@ -0,0 +1,95 @@ +# coding: utf-8 + +""" + cloudharness + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 1.0.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from typing import Optional, Set +from typing_extensions import Self + + +from cloudharness_model.base_model import CloudHarnessBaseModel +from pydantic import BaseModel, Field, field_validator, StrictStr, StrictBool, StrictInt, StrictFloat +from typing import ClassVar, List, Dict, Any, Union, Optional, Annotated +import importlib +from cloudharness_model.models.port_config import PortConfig + +class DatabaseConfig(CloudHarnessBaseModel): + """ + + """ # noqa: E501 + image: Optional[StrictStr] = None + name: Optional[StrictStr] = None + ports: Optional[List[PortConfig]] = None + additional_properties: Dict[str, Any] = {} + __properties: ClassVar[List[str]] = ["image", "name", "ports"] + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + * Fields in `self.additional_properties` are added to the output dict. + """ + excluded_fields: Set[str] = set([ + "additional_properties", + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in ports (list) + _items = [] + if self.ports: + for _item_ports in self.ports: + if _item_ports: + _items.append(_item_ports.to_dict()) + _dict['ports'] = _items + # puts key-value pairs in additional_properties in the top level + if self.additional_properties is not None: + for _key, _value in self.additional_properties.items(): + _dict[_key] = _value + + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of DatabaseConfig from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "image": obj.get("image"), + "name": obj.get("name"), + "ports": [PortConfig.from_dict(_item) for _item in obj["ports"]] if obj.get("ports") is not None else None + }) + # store additional fields in additional_properties + for _key in obj.keys(): + if _key not in cls.__properties: + _obj.additional_properties[_key] = obj.get(_key) + + return _obj + + diff --git a/libraries/models/cloudharness_model/models/database_deployment_config.py b/libraries/models/cloudharness_model/models/database_deployment_config.py index d87a45ad..04767742 100644 --- a/libraries/models/cloudharness_model/models/database_deployment_config.py +++ b/libraries/models/cloudharness_model/models/database_deployment_config.py @@ -42,8 +42,9 @@ class DatabaseDeploymentConfig(CloudHarnessBaseModel): postgres: Optional[Dict[str, Any]] = None neo4j: Optional[Any] = Field(default=None, description="Neo4j database specific configuration") resources: Optional[DeploymentResourcesConf] = None + external_connect_string: Optional[StrictStr] = Field(default=None, description="Specify if the database is external. If not null, auto deployment if set will not be used. Leva it as an empty string and the connect string will be provided as a secret to be provided at CI/CD (recommended)") additional_properties: Dict[str, Any] = {} - __properties: ClassVar[List[str]] = ["auto", "name", "type", "size", "user", "pass", "image_ref", "mongo", "postgres", "neo4j", "resources"] + __properties: ClassVar[List[str]] = ["auto", "name", "type", "size", "user", "pass", "image_ref", "mongo", "postgres", "neo4j", "resources", "external_connect_string"] @field_validator('type') def type_validate_regular_expression(cls, value): @@ -110,7 +111,8 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "mongo": obj.get("mongo"), "postgres": obj.get("postgres"), "neo4j": obj.get("neo4j"), - "resources": DeploymentResourcesConf.from_dict(obj["resources"]) if obj.get("resources") is not None else None + "resources": DeploymentResourcesConf.from_dict(obj["resources"]) if obj.get("resources") is not None else None, + "external_connect_string": obj.get("external_connect_string") }) # store additional fields in additional_properties for _key in obj.keys(): diff --git a/libraries/models/cloudharness_model/models/port_config.py b/libraries/models/cloudharness_model/models/port_config.py new file mode 100644 index 00000000..78ebc134 --- /dev/null +++ b/libraries/models/cloudharness_model/models/port_config.py @@ -0,0 +1,85 @@ +# coding: utf-8 + +""" + cloudharness + + No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + + The version of the OpenAPI document: 1.0.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from typing import Optional, Set +from typing_extensions import Self + + +from cloudharness_model.base_model import CloudHarnessBaseModel +from pydantic import BaseModel, Field, field_validator, StrictStr, StrictBool, StrictInt, StrictFloat +from typing import ClassVar, List, Dict, Any, Union, Optional, Annotated +import importlib + +class PortConfig(CloudHarnessBaseModel): + """ + + """ # noqa: E501 + name: Optional[StrictStr] = None + port: Optional[StrictInt] = None + additional_properties: Dict[str, Any] = {} + __properties: ClassVar[List[str]] = ["name", "port"] + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + * Fields in `self.additional_properties` are added to the output dict. + """ + excluded_fields: Set[str] = set([ + "additional_properties", + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # puts key-value pairs in additional_properties in the top level + if self.additional_properties is not None: + for _key, _value in self.additional_properties.items(): + _dict[_key] = _value + + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of PortConfig from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "name": obj.get("name"), + "port": obj.get("port") + }) + # store additional fields in additional_properties + for _key in obj.keys(): + if _key not in cls.__properties: + _obj.additional_properties[_key] = obj.get(_key) + + return _obj + + diff --git a/libraries/models/docs/DatabaseConfig.md b/libraries/models/docs/DatabaseConfig.md new file mode 100644 index 00000000..c6b3bf21 --- /dev/null +++ b/libraries/models/docs/DatabaseConfig.md @@ -0,0 +1,32 @@ +# DatabaseConfig + + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**image** | **str** | | [optional] +**name** | **str** | | [optional] +**ports** | [**List[PortConfig]**](PortConfig.md) | | [optional] + +## Example + +```python +from cloudharness_model.models.database_config import DatabaseConfig + +# TODO update the JSON string below +json = "{}" +# create an instance of DatabaseConfig from a JSON string +database_config_instance = DatabaseConfig.from_json(json) +# print the JSON string representation of the object +print(DatabaseConfig.to_json()) + +# convert the object into a dict +database_config_dict = database_config_instance.to_dict() +# create an instance of DatabaseConfig from a dict +database_config_from_dict = DatabaseConfig.from_dict(database_config_dict) +``` +[[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/libraries/models/docs/DatabaseDeploymentConfig.md b/libraries/models/docs/DatabaseDeploymentConfig.md index ae920cd1..f08b83fe 100644 --- a/libraries/models/docs/DatabaseDeploymentConfig.md +++ b/libraries/models/docs/DatabaseDeploymentConfig.md @@ -17,6 +17,7 @@ Name | Type | Description | Notes **postgres** | **Dict[str, object]** | | [optional] **neo4j** | **object** | Neo4j database specific configuration | [optional] **resources** | [**DeploymentResourcesConf**](DeploymentResourcesConf.md) | | [optional] +**external_connect_string** | **str** | Specify if the database is external. If not null, auto deployment if set will not be used. Leva it as an empty string and the connect string will be provided as a secret to be provided at CI/CD (recommended) | [optional] ## Example diff --git a/libraries/models/docs/PortConfig.md b/libraries/models/docs/PortConfig.md new file mode 100644 index 00000000..726c3c0a --- /dev/null +++ b/libraries/models/docs/PortConfig.md @@ -0,0 +1,31 @@ +# PortConfig + + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**name** | **str** | | [optional] +**port** | **int** | | [optional] + +## Example + +```python +from cloudharness_model.models.port_config import PortConfig + +# TODO update the JSON string below +json = "{}" +# create an instance of PortConfig from a JSON string +port_config_instance = PortConfig.from_json(json) +# print the JSON string representation of the object +print(PortConfig.to_json()) + +# convert the object into a dict +port_config_dict = port_config_instance.to_dict() +# create an instance of PortConfig from a dict +port_config_from_dict = PortConfig.from_dict(port_config_dict) +``` +[[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/tools/deployment-cli-tools/ch_cli_tools/codefresh.py b/tools/deployment-cli-tools/ch_cli_tools/codefresh.py index 1c520713..44e11779 100644 --- a/tools/deployment-cli-tools/ch_cli_tools/codefresh.py +++ b/tools/deployment-cli-tools/ch_cli_tools/codefresh.py @@ -400,6 +400,14 @@ def adjust_build_steps(index): secret_name = secret.replace("_", "__") arguments["custom_values"].append( "apps_%s_harness_secrets_%s=${{%s}}" % (app_name.replace("_", "__"), secret_name, secret_name.upper())) + # Add external_connect_string as a secret custom_value for apps that have it set to empty + for app_name, app in helm_values.apps.items(): + if app.harness.database and app.harness.database.get("external_connect_string") == "": + var_name = f"{app_name.upper().replace('-', '_')}_DB_EXTERNAL_CONNECT_STRING" + arguments["custom_values"].append( + "apps_%s_harness_database_external__connect__string=${{%s}}" % ( + app_name.replace("_", "__"), var_name) + ) # Add registry secret value secret if registry secret name is set registry = getattr(helm_values, "registry", None) secret = getattr(registry, "secret", None) From bc505ba1119a47b1c16f2062fef0e6f4b9d5787c Mon Sep 17 00:00:00 2001 From: Filippo Ledda Date: Mon, 9 Mar 2026 19:17:51 +0100 Subject: [PATCH 18/33] CH-248 add endpoint to test the external db --- applications/samples/api/openapi.yaml | 19 ++ .../samples/backend/.openapi-generator-ignore | 2 +- applications/samples/backend/requirements.txt | 22 +- .../controllers/database_controller.py | 20 ++ .../backend/samples/models/__init__.py | 1 + .../get_db_connect_string200_response.py | 61 +++++ .../backend/samples/openapi/openapi.yaml | 28 +- applications/samples/backend/samples/util.py | 10 +- applications/samples/deploy/values.yaml | 5 +- deployment-configuration/value-template.yaml | 2 + deployment/codefresh-test.yaml | 256 +++++++++--------- 11 files changed, 281 insertions(+), 145 deletions(-) create mode 100644 applications/samples/backend/samples/controllers/database_controller.py create mode 100644 applications/samples/backend/samples/models/get_db_connect_string200_response.py diff --git a/applications/samples/api/openapi.yaml b/applications/samples/api/openapi.yaml index f51b328d..24c9a026 100644 --- a/applications/samples/api/openapi.yaml +++ b/applications/samples/api/openapi.yaml @@ -83,6 +83,25 @@ paths: description: | Check if the token is valid x-openapi-router-controller: samples.controllers.auth_controller + /db-connect-string: + get: + tags: + - database + summary: Get database connection string + operationId: get_db_connect_string + description: Returns the database connection string for the current application. + responses: + "200": + description: Database connection string returned successfully + content: + application/json: + schema: + type: object + properties: + connect_string: + type: string + "500": + description: Error retrieving database connection string /sampleresources: summary: Path used to manage the list of sampleresources. description: >- diff --git a/applications/samples/backend/.openapi-generator-ignore b/applications/samples/backend/.openapi-generator-ignore index c89f830d..187605aa 100644 --- a/applications/samples/backend/.openapi-generator-ignore +++ b/applications/samples/backend/.openapi-generator-ignore @@ -22,7 +22,7 @@ # Then explicitly reverse the ignore rule for a single file: #!docs/README.md setup.py -*/controllers/* +# */controllers/* Dockerfile */__main__.py */test/* diff --git a/applications/samples/backend/requirements.txt b/applications/samples/backend/requirements.txt index 68760177..2cb06891 100644 --- a/applications/samples/backend/requirements.txt +++ b/applications/samples/backend/requirements.txt @@ -1,9 +1,13 @@ -connexion[swagger-ui,flask,uvicorn]>=3.0.0,<4.0.0 -swagger-ui-bundle>=1.1.0 -python_dateutil>=2.9.0 -setuptools>=21.0.0 -uvicorn -# Following some unnecessary requirements to make sure they can be installed -psycopg2-binary -sqlalchemy<2.0.0 -scipy \ No newline at end of file +connexion[swagger-ui] >= 2.6.0; python_version>="3.6" +# 2.3 is the last version that supports python 3.4-3.5 +connexion[swagger-ui] <= 2.3.0; python_version=="3.5" or python_version=="3.4" +# prevent breaking dependencies from advent of connexion>=3.0 +connexion[swagger-ui] <= 2.14.2; python_version>"3.4" +# connexion requires werkzeug but connexion < 2.4.0 does not install werkzeug +# we must peg werkzeug versions below to fix connexion +# https://github.com/zalando/connexion/pull/1044 +werkzeug == 0.16.1; python_version=="3.5" or python_version=="3.4" +swagger-ui-bundle >= 0.0.2 +python_dateutil >= 2.6.0 +setuptools >= 21.0.0 +Flask == 2.1.1 diff --git a/applications/samples/backend/samples/controllers/database_controller.py b/applications/samples/backend/samples/controllers/database_controller.py new file mode 100644 index 00000000..33750c74 --- /dev/null +++ b/applications/samples/backend/samples/controllers/database_controller.py @@ -0,0 +1,20 @@ +import connexion +from typing import Dict +from typing import Tuple +from typing import Union + +from samples.models.get_db_connect_string200_response import GetDbConnectString200Response # noqa: E501 +from samples import util + + +def get_db_connect_string(): # noqa: E501 + """Get database connection string + + Returns the database connection string for the current application. # noqa: E501 + + + :rtype: Union[GetDbConnectString200Response, Tuple[GetDbConnectString200Response, int], Tuple[GetDbConnectString200Response, int, Dict[str, str]] + """ + from cloudharness.applications import get_current_configuration + config = get_current_configuration() + return config.get_db_connection_string() diff --git a/applications/samples/backend/samples/models/__init__.py b/applications/samples/backend/samples/models/__init__.py index 94617398..e2138631 100644 --- a/applications/samples/backend/samples/models/__init__.py +++ b/applications/samples/backend/samples/models/__init__.py @@ -1,5 +1,6 @@ # flake8: noqa # import models into model package +from samples.models.get_db_connect_string200_response import GetDbConnectString200Response from samples.models.inline_response202 import InlineResponse202 from samples.models.inline_response202_task import InlineResponse202Task from samples.models.sample_resource import SampleResource diff --git a/applications/samples/backend/samples/models/get_db_connect_string200_response.py b/applications/samples/backend/samples/models/get_db_connect_string200_response.py new file mode 100644 index 00000000..9e4cae95 --- /dev/null +++ b/applications/samples/backend/samples/models/get_db_connect_string200_response.py @@ -0,0 +1,61 @@ +from datetime import date, datetime # noqa: F401 + +from typing import List, Dict # noqa: F401 + +from samples.models.base_model import Model +from samples import util + + +class GetDbConnectString200Response(Model): + """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + + Do not edit the class manually. + """ + + def __init__(self, connect_string=None): # noqa: E501 + """GetDbConnectString200Response - a model defined in OpenAPI + + :param connect_string: The connect_string of this GetDbConnectString200Response. # noqa: E501 + :type connect_string: str + """ + self.openapi_types = { + 'connect_string': str + } + + self.attribute_map = { + 'connect_string': 'connect_string' + } + + self._connect_string = connect_string + + @classmethod + def from_dict(cls, dikt) -> 'GetDbConnectString200Response': + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The get_db_connect_string_200_response of this GetDbConnectString200Response. # noqa: E501 + :rtype: GetDbConnectString200Response + """ + return util.deserialize_model(dikt, cls) + + @property + def connect_string(self) -> str: + """Gets the connect_string of this GetDbConnectString200Response. + + + :return: The connect_string of this GetDbConnectString200Response. + :rtype: str + """ + return self._connect_string + + @connect_string.setter + def connect_string(self, connect_string: str): + """Sets the connect_string of this GetDbConnectString200Response. + + + :param connect_string: The connect_string of this GetDbConnectString200Response. + :type connect_string: str + """ + + self._connect_string = connect_string diff --git a/applications/samples/backend/samples/openapi/openapi.yaml b/applications/samples/backend/samples/openapi/openapi.yaml index 401ba448..ef83e8be 100644 --- a/applications/samples/backend/samples/openapi/openapi.yaml +++ b/applications/samples/backend/samples/openapi/openapi.yaml @@ -15,6 +15,23 @@ tags: - description: "" name: resource paths: + /db-connect-string: + get: + description: Returns the database connection string for the current application. + operationId: get_db_connect_string + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/get_db_connect_string_200_response' + description: Database connection string returned successfully + "500": + description: Error retrieving database connection string + summary: Get database connection string + tags: + - database + x-openapi-router-controller: samples.controllers.database_controller /error: get: operationId: error @@ -326,6 +343,15 @@ components: - a title: SampleResource type: object + get_db_connect_string_200_response: + example: + connect_string: connect_string + properties: + connect_string: + title: connect_string + type: string + title: get_db_connect_string_200_response + type: object securitySchemes: bearerAuth: bearerFormat: JWT @@ -336,4 +362,4 @@ components: in: cookie name: kc-access type: apiKey - x-apikeyInfoFunc: samples.controllers.security_controller_.info_from_cookieAuth + x-apikeyInfoFunc: cloudharness.auth.decode_token diff --git a/applications/samples/backend/samples/util.py b/applications/samples/backend/samples/util.py index b802fafd..5b241814 100644 --- a/applications/samples/backend/samples/util.py +++ b/applications/samples/backend/samples/util.py @@ -67,8 +67,8 @@ def deserialize_date(string): :rtype: date """ if string is None: - return None - + return None + try: from dateutil.parser import parse return parse(string).date() @@ -87,8 +87,8 @@ def deserialize_datetime(string): :rtype: datetime """ if string is None: - return None - + return None + try: from dateutil.parser import parse return parse(string) @@ -144,4 +144,4 @@ def _deserialize_dict(data, boxed_type): :rtype: dict """ return {k: _deserialize(v, boxed_type) - for k, v in data.items()} + for k, v in data.items() } diff --git a/applications/samples/deploy/values.yaml b/applications/samples/deploy/values.yaml index 55bcb0d4..54da46a7 100644 --- a/applications/samples/deploy/values.yaml +++ b/applications/samples/deploy/values.yaml @@ -18,7 +18,7 @@ harness: size: 10Mi usenfs: false auto: true - port: 8080 + port: 8080 proxy: gatekeeper: replicas: 1 @@ -98,3 +98,6 @@ harness: dockerfile: buildArgs: TEST_ARGUMENT: example value + database: + type: postgres + external_connect_string: "test connection string" \ No newline at end of file diff --git a/deployment-configuration/value-template.yaml b/deployment-configuration/value-template.yaml index fdf11655..a7444df5 100644 --- a/deployment-configuration/value-template.yaml +++ b/deployment-configuration/value-template.yaml @@ -77,6 +77,8 @@ harness: image_ref: # -- expose database to the public with ingress expose: false + # -- Set to "" to set the set the string in the CI/CD as a secret. Only set the full value for dev/testing + external_connect_string: # -- settings for mongo database (for type==mongo) mongo: image: mongo:5 diff --git a/deployment/codefresh-test.yaml b/deployment/codefresh-test.yaml index 95d35e59..0012e877 100644 --- a/deployment/codefresh-test.yaml +++ b/deployment/codefresh-test.yaml @@ -47,7 +47,7 @@ steps: type: parallel stage: build steps: - test-e2e: + accounts: type: build stage: build dockerfile: Dockerfile @@ -55,20 +55,41 @@ steps: buildkit: true build_arguments: - NOCACHE=${{CF_BUILD_ID}} - image_name: cloud-harness/test-e2e - title: Test e2e - working_directory: ./test/test-e2e + image_name: cloud-harness/accounts + title: Accounts + working_directory: ./applications/accounts tags: - - '${{TEST_E2E_TAG}}' + - '${{ACCOUNTS_TAG}}' - '${{DEPLOYMENT_PUBLISH_TAG}}-dev' - '${{CF_BRANCH_TAG_NORMALIZED_LOWER_CASE}}' - - latest when: condition: any: - buildDoesNotExist: includes('${{TEST_E2E_TAG_EXISTS}}', '{{TEST_E2E_TAG_EXISTS}}') + buildDoesNotExist: includes('${{ACCOUNTS_TAG_EXISTS}}', '{{ACCOUNTS_TAG_EXISTS}}') == true - forceNoCache: includes('${{TEST_E2E_TAG_FORCE_BUILD}}', '{{TEST_E2E_TAG_FORCE_BUILD}}') + forceNoCache: includes('${{ACCOUNTS_TAG_FORCE_BUILD}}', '{{ACCOUNTS_TAG_FORCE_BUILD}}') + == false + cloudharness-base: + type: build + stage: build + dockerfile: infrastructure/base-images/cloudharness-base/Dockerfile + registry: '${{CODEFRESH_REGISTRY}}' + buildkit: true + build_arguments: + - NOCACHE=${{CF_BUILD_ID}} + image_name: cloud-harness/cloudharness-base + title: Cloudharness base + working_directory: ./. + tags: + - '${{CLOUDHARNESS_BASE_TAG}}' + - '${{DEPLOYMENT_PUBLISH_TAG}}-dev' + - '${{CF_BRANCH_TAG_NORMALIZED_LOWER_CASE}}' + when: + condition: + any: + buildDoesNotExist: includes('${{CLOUDHARNESS_BASE_TAG_EXISTS}}', '{{CLOUDHARNESS_BASE_TAG_EXISTS}}') + == true + forceNoCache: includes('${{CLOUDHARNESS_BASE_TAG_FORCE_BUILD}}', '{{CLOUDHARNESS_BASE_TAG_FORCE_BUILD}}') == false cloudharness-frontend-build: type: build @@ -92,7 +113,7 @@ steps: '{{CLOUDHARNESS_FRONTEND_BUILD_TAG_EXISTS}}') == true forceNoCache: includes('${{CLOUDHARNESS_FRONTEND_BUILD_TAG_FORCE_BUILD}}', '{{CLOUDHARNESS_FRONTEND_BUILD_TAG_FORCE_BUILD}}') == false - accounts: + test-e2e: type: build stage: build dockerfile: Dockerfile @@ -100,48 +121,50 @@ steps: buildkit: true build_arguments: - NOCACHE=${{CF_BUILD_ID}} - image_name: cloud-harness/accounts - title: Accounts - working_directory: ./applications/accounts + image_name: cloud-harness/test-e2e + title: Test e2e + working_directory: ./test/test-e2e tags: - - '${{ACCOUNTS_TAG}}' + - '${{TEST_E2E_TAG}}' - '${{DEPLOYMENT_PUBLISH_TAG}}-dev' - '${{CF_BRANCH_TAG_NORMALIZED_LOWER_CASE}}' + - latest when: condition: any: - buildDoesNotExist: includes('${{ACCOUNTS_TAG_EXISTS}}', '{{ACCOUNTS_TAG_EXISTS}}') + buildDoesNotExist: includes('${{TEST_E2E_TAG_EXISTS}}', '{{TEST_E2E_TAG_EXISTS}}') == true - forceNoCache: includes('${{ACCOUNTS_TAG_FORCE_BUILD}}', '{{ACCOUNTS_TAG_FORCE_BUILD}}') + forceNoCache: includes('${{TEST_E2E_TAG_FORCE_BUILD}}', '{{TEST_E2E_TAG_FORCE_BUILD}}') == false - cloudharness-base: + title: Build parallel step 1 + build_application_images_1: + type: parallel + stage: build + steps: + cloudharness-django: type: build stage: build - dockerfile: infrastructure/base-images/cloudharness-base/Dockerfile + dockerfile: Dockerfile registry: '${{CODEFRESH_REGISTRY}}' buildkit: true build_arguments: - NOCACHE=${{CF_BUILD_ID}} - image_name: cloud-harness/cloudharness-base - title: Cloudharness base - working_directory: ./. + - CLOUDHARNESS_BASE=${{REGISTRY}}/cloud-harness/cloudharness-base:${{CLOUDHARNESS_BASE_TAG}} + image_name: cloud-harness/cloudharness-django + title: Cloudharness django + working_directory: ./infrastructure/common-images/cloudharness-django tags: - - '${{CLOUDHARNESS_BASE_TAG}}' + - '${{CLOUDHARNESS_DJANGO_TAG}}' - '${{DEPLOYMENT_PUBLISH_TAG}}-dev' - '${{CF_BRANCH_TAG_NORMALIZED_LOWER_CASE}}' when: condition: any: - buildDoesNotExist: includes('${{CLOUDHARNESS_BASE_TAG_EXISTS}}', '{{CLOUDHARNESS_BASE_TAG_EXISTS}}') + buildDoesNotExist: includes('${{CLOUDHARNESS_DJANGO_TAG_EXISTS}}', '{{CLOUDHARNESS_DJANGO_TAG_EXISTS}}') == true - forceNoCache: includes('${{CLOUDHARNESS_BASE_TAG_FORCE_BUILD}}', '{{CLOUDHARNESS_BASE_TAG_FORCE_BUILD}}') + forceNoCache: includes('${{CLOUDHARNESS_DJANGO_TAG_FORCE_BUILD}}', '{{CLOUDHARNESS_DJANGO_TAG_FORCE_BUILD}}') == false - title: Build parallel step 1 - build_application_images_1: - type: parallel - stage: build - steps: - samples-print-file: + cloudharness-flask: type: build stage: build dockerfile: Dockerfile @@ -150,21 +173,21 @@ steps: build_arguments: - NOCACHE=${{CF_BUILD_ID}} - CLOUDHARNESS_BASE=${{REGISTRY}}/cloud-harness/cloudharness-base:${{CLOUDHARNESS_BASE_TAG}} - image_name: cloud-harness/sampleapp-print-file - title: Samples print file - working_directory: ./applications/samples/tasks/print-file + image_name: cloud-harness/cloudharness-flask + title: Cloudharness flask + working_directory: ./infrastructure/common-images/cloudharness-flask tags: - - '${{SAMPLES_PRINT_FILE_TAG}}' + - '${{CLOUDHARNESS_FLASK_TAG}}' - '${{DEPLOYMENT_PUBLISH_TAG}}-dev' - '${{CF_BRANCH_TAG_NORMALIZED_LOWER_CASE}}' when: condition: any: - buildDoesNotExist: includes('${{SAMPLES_PRINT_FILE_TAG_EXISTS}}', '{{SAMPLES_PRINT_FILE_TAG_EXISTS}}') + buildDoesNotExist: includes('${{CLOUDHARNESS_FLASK_TAG_EXISTS}}', '{{CLOUDHARNESS_FLASK_TAG_EXISTS}}') == true - forceNoCache: includes('${{SAMPLES_PRINT_FILE_TAG_FORCE_BUILD}}', '{{SAMPLES_PRINT_FILE_TAG_FORCE_BUILD}}') + forceNoCache: includes('${{CLOUDHARNESS_FLASK_TAG_FORCE_BUILD}}', '{{CLOUDHARNESS_FLASK_TAG_FORCE_BUILD}}') == false - workflows-notify-queue: + jupyterhub: type: build stage: build dockerfile: Dockerfile @@ -173,21 +196,21 @@ steps: build_arguments: - NOCACHE=${{CF_BUILD_ID}} - CLOUDHARNESS_BASE=${{REGISTRY}}/cloud-harness/cloudharness-base:${{CLOUDHARNESS_BASE_TAG}} - image_name: cloud-harness/workflows-notify-queue - title: Workflows notify queue - working_directory: ./applications/workflows/tasks/notify-queue + image_name: cloud-harness/jupyterhub + title: Jupyterhub + working_directory: ./applications/jupyterhub tags: - - '${{WORKFLOWS_NOTIFY_QUEUE_TAG}}' + - '${{JUPYTERHUB_TAG}}' - '${{DEPLOYMENT_PUBLISH_TAG}}-dev' - '${{CF_BRANCH_TAG_NORMALIZED_LOWER_CASE}}' when: condition: any: - buildDoesNotExist: includes('${{WORKFLOWS_NOTIFY_QUEUE_TAG_EXISTS}}', - '{{WORKFLOWS_NOTIFY_QUEUE_TAG_EXISTS}}') == true - forceNoCache: includes('${{WORKFLOWS_NOTIFY_QUEUE_TAG_FORCE_BUILD}}', - '{{WORKFLOWS_NOTIFY_QUEUE_TAG_FORCE_BUILD}}') == false - workflows-send-result-event: + buildDoesNotExist: includes('${{JUPYTERHUB_TAG_EXISTS}}', '{{JUPYTERHUB_TAG_EXISTS}}') + == true + forceNoCache: includes('${{JUPYTERHUB_TAG_FORCE_BUILD}}', '{{JUPYTERHUB_TAG_FORCE_BUILD}}') + == false + samples-print-file: type: build stage: build dockerfile: Dockerfile @@ -196,21 +219,21 @@ steps: build_arguments: - NOCACHE=${{CF_BUILD_ID}} - CLOUDHARNESS_BASE=${{REGISTRY}}/cloud-harness/cloudharness-base:${{CLOUDHARNESS_BASE_TAG}} - image_name: cloud-harness/workflows-send-result-event - title: Workflows send result event - working_directory: ./applications/workflows/tasks/send-result-event + image_name: cloud-harness/sampleapp-print-file + title: Samples print file + working_directory: ./applications/samples/tasks/print-file tags: - - '${{WORKFLOWS_SEND_RESULT_EVENT_TAG}}' + - '${{SAMPLES_PRINT_FILE_TAG}}' - '${{DEPLOYMENT_PUBLISH_TAG}}-dev' - '${{CF_BRANCH_TAG_NORMALIZED_LOWER_CASE}}' when: condition: any: - buildDoesNotExist: includes('${{WORKFLOWS_SEND_RESULT_EVENT_TAG_EXISTS}}', - '{{WORKFLOWS_SEND_RESULT_EVENT_TAG_EXISTS}}') == true - forceNoCache: includes('${{WORKFLOWS_SEND_RESULT_EVENT_TAG_FORCE_BUILD}}', - '{{WORKFLOWS_SEND_RESULT_EVENT_TAG_FORCE_BUILD}}') == false - cloudharness-flask: + buildDoesNotExist: includes('${{SAMPLES_PRINT_FILE_TAG_EXISTS}}', '{{SAMPLES_PRINT_FILE_TAG_EXISTS}}') + == true + forceNoCache: includes('${{SAMPLES_PRINT_FILE_TAG_FORCE_BUILD}}', '{{SAMPLES_PRINT_FILE_TAG_FORCE_BUILD}}') + == false + samples-secret: type: build stage: build dockerfile: Dockerfile @@ -219,19 +242,19 @@ steps: build_arguments: - NOCACHE=${{CF_BUILD_ID}} - CLOUDHARNESS_BASE=${{REGISTRY}}/cloud-harness/cloudharness-base:${{CLOUDHARNESS_BASE_TAG}} - image_name: cloud-harness/cloudharness-flask - title: Cloudharness flask - working_directory: ./infrastructure/common-images/cloudharness-flask + image_name: cloud-harness/sampleapp-secret + title: Samples secret + working_directory: ./applications/samples/tasks/secret tags: - - '${{CLOUDHARNESS_FLASK_TAG}}' + - '${{SAMPLES_SECRET_TAG}}' - '${{DEPLOYMENT_PUBLISH_TAG}}-dev' - '${{CF_BRANCH_TAG_NORMALIZED_LOWER_CASE}}' when: condition: any: - buildDoesNotExist: includes('${{CLOUDHARNESS_FLASK_TAG_EXISTS}}', '{{CLOUDHARNESS_FLASK_TAG_EXISTS}}') + buildDoesNotExist: includes('${{SAMPLES_SECRET_TAG_EXISTS}}', '{{SAMPLES_SECRET_TAG_EXISTS}}') == true - forceNoCache: includes('${{CLOUDHARNESS_FLASK_TAG_FORCE_BUILD}}', '{{CLOUDHARNESS_FLASK_TAG_FORCE_BUILD}}') + forceNoCache: includes('${{SAMPLES_SECRET_TAG_FORCE_BUILD}}', '{{SAMPLES_SECRET_TAG_FORCE_BUILD}}') == false test-api: type: build @@ -257,29 +280,6 @@ steps: == true forceNoCache: includes('${{TEST_API_TAG_FORCE_BUILD}}', '{{TEST_API_TAG_FORCE_BUILD}}') == false - cloudharness-django: - type: build - stage: build - dockerfile: Dockerfile - registry: '${{CODEFRESH_REGISTRY}}' - buildkit: true - build_arguments: - - NOCACHE=${{CF_BUILD_ID}} - - CLOUDHARNESS_BASE=${{REGISTRY}}/cloud-harness/cloudharness-base:${{CLOUDHARNESS_BASE_TAG}} - image_name: cloud-harness/cloudharness-django - title: Cloudharness django - working_directory: ./infrastructure/common-images/cloudharness-django - tags: - - '${{CLOUDHARNESS_DJANGO_TAG}}' - - '${{DEPLOYMENT_PUBLISH_TAG}}-dev' - - '${{CF_BRANCH_TAG_NORMALIZED_LOWER_CASE}}' - when: - condition: - any: - buildDoesNotExist: includes('${{CLOUDHARNESS_DJANGO_TAG_EXISTS}}', '{{CLOUDHARNESS_DJANGO_TAG_EXISTS}}') - == true - forceNoCache: includes('${{CLOUDHARNESS_DJANGO_TAG_FORCE_BUILD}}', '{{CLOUDHARNESS_DJANGO_TAG_FORCE_BUILD}}') - == false workflows-extract-download: type: build stage: build @@ -303,7 +303,7 @@ steps: '{{WORKFLOWS_EXTRACT_DOWNLOAD_TAG_EXISTS}}') == true forceNoCache: includes('${{WORKFLOWS_EXTRACT_DOWNLOAD_TAG_FORCE_BUILD}}', '{{WORKFLOWS_EXTRACT_DOWNLOAD_TAG_FORCE_BUILD}}') == false - jupyterhub: + workflows-notify-queue: type: build stage: build dockerfile: Dockerfile @@ -312,21 +312,21 @@ steps: build_arguments: - NOCACHE=${{CF_BUILD_ID}} - CLOUDHARNESS_BASE=${{REGISTRY}}/cloud-harness/cloudharness-base:${{CLOUDHARNESS_BASE_TAG}} - image_name: cloud-harness/jupyterhub - title: Jupyterhub - working_directory: ./applications/jupyterhub + image_name: cloud-harness/workflows-notify-queue + title: Workflows notify queue + working_directory: ./applications/workflows/tasks/notify-queue tags: - - '${{JUPYTERHUB_TAG}}' + - '${{WORKFLOWS_NOTIFY_QUEUE_TAG}}' - '${{DEPLOYMENT_PUBLISH_TAG}}-dev' - '${{CF_BRANCH_TAG_NORMALIZED_LOWER_CASE}}' when: condition: any: - buildDoesNotExist: includes('${{JUPYTERHUB_TAG_EXISTS}}', '{{JUPYTERHUB_TAG_EXISTS}}') - == true - forceNoCache: includes('${{JUPYTERHUB_TAG_FORCE_BUILD}}', '{{JUPYTERHUB_TAG_FORCE_BUILD}}') - == false - samples-secret: + buildDoesNotExist: includes('${{WORKFLOWS_NOTIFY_QUEUE_TAG_EXISTS}}', + '{{WORKFLOWS_NOTIFY_QUEUE_TAG_EXISTS}}') == true + forceNoCache: includes('${{WORKFLOWS_NOTIFY_QUEUE_TAG_FORCE_BUILD}}', + '{{WORKFLOWS_NOTIFY_QUEUE_TAG_FORCE_BUILD}}') == false + workflows-send-result-event: type: build stage: build dockerfile: Dockerfile @@ -335,48 +335,25 @@ steps: build_arguments: - NOCACHE=${{CF_BUILD_ID}} - CLOUDHARNESS_BASE=${{REGISTRY}}/cloud-harness/cloudharness-base:${{CLOUDHARNESS_BASE_TAG}} - image_name: cloud-harness/sampleapp-secret - title: Samples secret - working_directory: ./applications/samples/tasks/secret + image_name: cloud-harness/workflows-send-result-event + title: Workflows send result event + working_directory: ./applications/workflows/tasks/send-result-event tags: - - '${{SAMPLES_SECRET_TAG}}' + - '${{WORKFLOWS_SEND_RESULT_EVENT_TAG}}' - '${{DEPLOYMENT_PUBLISH_TAG}}-dev' - '${{CF_BRANCH_TAG_NORMALIZED_LOWER_CASE}}' when: condition: any: - buildDoesNotExist: includes('${{SAMPLES_SECRET_TAG_EXISTS}}', '{{SAMPLES_SECRET_TAG_EXISTS}}') - == true - forceNoCache: includes('${{SAMPLES_SECRET_TAG_FORCE_BUILD}}', '{{SAMPLES_SECRET_TAG_FORCE_BUILD}}') - == false + buildDoesNotExist: includes('${{WORKFLOWS_SEND_RESULT_EVENT_TAG_EXISTS}}', + '{{WORKFLOWS_SEND_RESULT_EVENT_TAG_EXISTS}}') == true + forceNoCache: includes('${{WORKFLOWS_SEND_RESULT_EVENT_TAG_FORCE_BUILD}}', + '{{WORKFLOWS_SEND_RESULT_EVENT_TAG_FORCE_BUILD}}') == false title: Build parallel step 2 build_application_images_2: type: parallel stage: build steps: - workflows: - type: build - stage: build - dockerfile: Dockerfile - registry: '${{CODEFRESH_REGISTRY}}' - buildkit: true - build_arguments: - - NOCACHE=${{CF_BUILD_ID}} - - CLOUDHARNESS_FLASK=${{REGISTRY}}/cloud-harness/cloudharness-flask:${{CLOUDHARNESS_FLASK_TAG}} - image_name: cloud-harness/workflows - title: Workflows - working_directory: ./applications/workflows/server - tags: - - '${{WORKFLOWS_TAG}}' - - '${{DEPLOYMENT_PUBLISH_TAG}}-dev' - - '${{CF_BRANCH_TAG_NORMALIZED_LOWER_CASE}}' - when: - condition: - any: - buildDoesNotExist: includes('${{WORKFLOWS_TAG_EXISTS}}', '{{WORKFLOWS_TAG_EXISTS}}') - == true - forceNoCache: includes('${{WORKFLOWS_TAG_FORCE_BUILD}}', '{{WORKFLOWS_TAG_FORCE_BUILD}}') - == false common: type: build stage: build @@ -447,6 +424,29 @@ steps: == true forceNoCache: includes('${{VOLUMEMANAGER_TAG_FORCE_BUILD}}', '{{VOLUMEMANAGER_TAG_FORCE_BUILD}}') == false + workflows: + type: build + stage: build + dockerfile: Dockerfile + registry: '${{CODEFRESH_REGISTRY}}' + buildkit: true + build_arguments: + - NOCACHE=${{CF_BUILD_ID}} + - CLOUDHARNESS_FLASK=${{REGISTRY}}/cloud-harness/cloudharness-flask:${{CLOUDHARNESS_FLASK_TAG}} + image_name: cloud-harness/workflows + title: Workflows + working_directory: ./applications/workflows/server + tags: + - '${{WORKFLOWS_TAG}}' + - '${{DEPLOYMENT_PUBLISH_TAG}}-dev' + - '${{CF_BRANCH_TAG_NORMALIZED_LOWER_CASE}}' + when: + condition: + any: + buildDoesNotExist: includes('${{WORKFLOWS_TAG_EXISTS}}', '{{WORKFLOWS_TAG_EXISTS}}') + == true + forceNoCache: includes('${{WORKFLOWS_TAG_FORCE_BUILD}}', '{{WORKFLOWS_TAG_FORCE_BUILD}}') + == false title: Build parallel step 3 build_application_images_3: type: parallel @@ -509,13 +509,13 @@ steps: commands: - kubectl config use-context ${{CLUSTER_NAME}} - kubectl config set-context --current --namespace=test-${{NAMESPACE_BASENAME}} - - kubectl rollout status deployment/workflows - - kubectl rollout status deployment/common + - kubectl rollout status deployment/volumemanager + - kubectl rollout status deployment/argo-gk - kubectl rollout status deployment/samples - kubectl rollout status deployment/samples-gk - - kubectl rollout status deployment/argo-gk - - kubectl rollout status deployment/volumemanager - kubectl rollout status deployment/accounts + - kubectl rollout status deployment/common + - kubectl rollout status deployment/workflows - sleep 60 tests_api: stage: qa From 7ea8c0fa46ccb91c9c17baa80f7068ad7a21d6e7 Mon Sep 17 00:00:00 2001 From: Filippo Ledda Date: Mon, 9 Mar 2026 20:07:18 +0100 Subject: [PATCH 19/33] CH-224 GK encryption key to AES-256 --- deployment-configuration/helm/templates/auto-gatekeepers.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment-configuration/helm/templates/auto-gatekeepers.yaml b/deployment-configuration/helm/templates/auto-gatekeepers.yaml index ce075f02..1ceadedb 100644 --- a/deployment-configuration/helm/templates/auto-gatekeepers.yaml +++ b/deployment-configuration/helm/templates/auto-gatekeepers.yaml @@ -124,7 +124,7 @@ stringData: {{- end }} {{- end }} {{- if not $hasExisting }} - encryption-key: {{ randAlphaNum 20 | quote }} + encryption-key: {{ randAlphaNum 32 | quote }} {{- end }} {{- end }} --- From c8a7208ef5c125cedb184e88b6bde3ad37164634 Mon Sep 17 00:00:00 2001 From: Filippo Ledda Date: Mon, 9 Mar 2026 20:08:57 +0100 Subject: [PATCH 20/33] CH-248 add test coverage for CI/CD secret set --- .../applications/myapp/deploy/values.yaml | 4 +++ .../tests/test_codefresh.py | 34 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/tools/deployment-cli-tools/tests/resources/applications/myapp/deploy/values.yaml b/tools/deployment-cli-tools/tests/resources/applications/myapp/deploy/values.yaml index e386702a..8c662f66 100644 --- a/tools/deployment-cli-tools/tests/resources/applications/myapp/deploy/values.yaml +++ b/tools/deployment-cli-tools/tests/resources/applications/myapp/deploy/values.yaml @@ -1,6 +1,10 @@ harness: name: "I'm useless" subdomain: mysubdomain + database: + auto: true + type: mongo + external_connect_string: "" dependencies: soft: - legacy diff --git a/tools/deployment-cli-tools/tests/test_codefresh.py b/tools/deployment-cli-tools/tests/test_codefresh.py index 65bd1fc2..ee9f158d 100644 --- a/tools/deployment-cli-tools/tests/test_codefresh.py +++ b/tools/deployment-cli-tools/tests/test_codefresh.py @@ -331,6 +331,40 @@ def test_create_codefresh_configuration_nobuild(): assert "publish_myapp-mytask" in l1_steps["publish"]["steps"] +def test_codefresh_db_external_connect_string_secret(): + """When an app has database.external_connect_string set to '', a custom_values entry must be added to the deployment step.""" + values = create_helm_chart( + [CLOUDHARNESS_ROOT, RESOURCES], + output_path=OUT, + include=['myapp'], + exclude=['events'], + domain="my.local", + namespace='test', + env='dev', + local=False, + tag=1, + registry='reg' + ) + try: + root_paths = preprocess_build_overrides( + root_paths=[CLOUDHARNESS_ROOT, RESOURCES], + helm_values=values, + merge_build_path=BUILD_MERGE_DIR + ) + build_included = [app['harness']['name'] + for app in values['apps'].values() if 'harness' in app] + cf = create_codefresh_deployment_scripts(root_paths, include=build_included, + envs=['dev'], + base_image_name=values['name'], + helm_values=values, save=False) + custom_values = cf['steps']['deployment']['arguments']['custom_values'] + expected = "apps_myapp_harness_database_external__connect__string=${{MYAPP_DB_EXTERNAL_CONNECT_STRING}}" + assert expected in custom_values, \ + f"Expected custom_value entry for external_connect_string not found. Got: {custom_values}" + finally: + shutil.rmtree(BUILD_MERGE_DIR, ignore_errors=True) + + def test_sort_parallel_steps_alphabetically(): """Sub-steps inside parallel steps must be sorted alphabetically by name.""" steps = { From 7387a1d91a4f062462c2fdefd41280f43fc6796f Mon Sep 17 00:00:00 2001 From: Filippo Ledda Date: Mon, 9 Mar 2026 20:16:53 +0100 Subject: [PATCH 21/33] CH-248 fix helm templates guard --- deployment-configuration/helm/templates/auto-deployments.yaml | 4 ++-- deployment-configuration/helm/templates/auto-secrets.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/deployment-configuration/helm/templates/auto-deployments.yaml b/deployment-configuration/helm/templates/auto-deployments.yaml index f3be4850..92e5b944 100644 --- a/deployment-configuration/helm/templates/auto-deployments.yaml +++ b/deployment-configuration/helm/templates/auto-deployments.yaml @@ -140,7 +140,7 @@ spec: mountPath: "/opt/cloudharness/resources/secrets/{{ .app.harness.name }}" readOnly: true {{- end }} - {{- if and .app.harness.database .app.harness.database.external_connect_string }} + {{- if and .app.harness.database (hasKey .app.harness.database "external_connect_string") .app.harness.database.external_connect_string }} - name: db-external mountPath: "/opt/cloudharness/resources/db" readOnly: true @@ -174,7 +174,7 @@ spec: secret: secretName: {{ .app.harness.deployment.name }} {{- end }} - {{- if and .app.harness.database .app.harness.database.external_connect_string }} + {{- if and .app.harness.database (hasKey .app.harness.database "external_connect_string") .app.harness.database.external_connect_string }} - name: db-external secret: secretName: {{ printf "%s-db" .app.harness.deployment.name }} diff --git a/deployment-configuration/helm/templates/auto-secrets.yaml b/deployment-configuration/helm/templates/auto-secrets.yaml index 2254391b..891a7c4f 100644 --- a/deployment-configuration/helm/templates/auto-secrets.yaml +++ b/deployment-configuration/helm/templates/auto-secrets.yaml @@ -73,7 +73,7 @@ stringData: --- {{- end }} {{- range $app := .Values.apps }} - {{- if and $app.harness.database $app.harness.database.external_connect_string }} + {{- if and $app.harness.database (hasKey $app.harness.database "external_connect_string") $app.harness.database.external_connect_string }} {{- include "deploy_utils.db_external_secret" (dict "root" $ "app" $app) }} {{- end }} {{- end }} \ No newline at end of file From 1ee00c5e675dbb7889832e0be402e820d8fe9dd1 Mon Sep 17 00:00:00 2001 From: Filippo Ledda Date: Mon, 9 Mar 2026 20:22:50 +0100 Subject: [PATCH 22/33] CH-248 fix helm templates guard --- deployment-configuration/helm/templates/auto-deployments.yaml | 4 ++-- deployment-configuration/helm/templates/auto-secrets.yaml | 2 +- deployment-configuration/helm/templates/configmap.yaml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/deployment-configuration/helm/templates/auto-deployments.yaml b/deployment-configuration/helm/templates/auto-deployments.yaml index 92e5b944..b0cd55a5 100644 --- a/deployment-configuration/helm/templates/auto-deployments.yaml +++ b/deployment-configuration/helm/templates/auto-deployments.yaml @@ -140,7 +140,7 @@ spec: mountPath: "/opt/cloudharness/resources/secrets/{{ .app.harness.name }}" readOnly: true {{- end }} - {{- if and .app.harness.database (hasKey .app.harness.database "external_connect_string") .app.harness.database.external_connect_string }} + {{- if and .app.harness.database (kindIs "map" .app.harness.database) (hasKey .app.harness.database "external_connect_string") .app.harness.database.external_connect_string }} - name: db-external mountPath: "/opt/cloudharness/resources/db" readOnly: true @@ -174,7 +174,7 @@ spec: secret: secretName: {{ .app.harness.deployment.name }} {{- end }} - {{- if and .app.harness.database (hasKey .app.harness.database "external_connect_string") .app.harness.database.external_connect_string }} + {{- if and .app.harness.database (kindIs "map" .app.harness.database) (hasKey .app.harness.database "external_connect_string") .app.harness.database.external_connect_string }} - name: db-external secret: secretName: {{ printf "%s-db" .app.harness.deployment.name }} diff --git a/deployment-configuration/helm/templates/auto-secrets.yaml b/deployment-configuration/helm/templates/auto-secrets.yaml index 891a7c4f..8cad6353 100644 --- a/deployment-configuration/helm/templates/auto-secrets.yaml +++ b/deployment-configuration/helm/templates/auto-secrets.yaml @@ -73,7 +73,7 @@ stringData: --- {{- end }} {{- range $app := .Values.apps }} - {{- if and $app.harness.database (hasKey $app.harness.database "external_connect_string") $app.harness.database.external_connect_string }} + {{- if and $app.harness.database (kindIs "map" $app.harness.database) (hasKey $app.harness.database "external_connect_string") $app.harness.database.external_connect_string }} {{- include "deploy_utils.db_external_secret" (dict "root" $ "app" $app) }} {{- end }} {{- end }} \ No newline at end of file diff --git a/deployment-configuration/helm/templates/configmap.yaml b/deployment-configuration/helm/templates/configmap.yaml index 47076a05..c6db7693 100644 --- a/deployment-configuration/helm/templates/configmap.yaml +++ b/deployment-configuration/helm/templates/configmap.yaml @@ -20,7 +20,7 @@ data: {{- range $key, $val := .Values.apps }} {{- $app := get $values_copy.apps $key }} {{- $tmp := set $app.harness "secrets" dict }} - {{- if $app.harness.database }} + {{- if and $app.harness.database (kindIs "map" $app.harness.database) }} {{- $tmp := unset $app.harness.database "external_connect_string" }} {{- end }} {{- end }} From be1d73740ceddff9a4b7db3e0cf66fc39eac91fd Mon Sep 17 00:00:00 2001 From: Filippo Ledda Date: Mon, 9 Mar 2026 20:28:58 +0100 Subject: [PATCH 23/33] CH-248 fix helm templates guard --- .../helm/templates/auto-deployments.yaml | 8 ++++++-- deployment-configuration/helm/templates/auto-secrets.yaml | 4 +++- deployment-configuration/helm/templates/configmap.yaml | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/deployment-configuration/helm/templates/auto-deployments.yaml b/deployment-configuration/helm/templates/auto-deployments.yaml index b0cd55a5..30bb9083 100644 --- a/deployment-configuration/helm/templates/auto-deployments.yaml +++ b/deployment-configuration/helm/templates/auto-deployments.yaml @@ -140,10 +140,12 @@ spec: mountPath: "/opt/cloudharness/resources/secrets/{{ .app.harness.name }}" readOnly: true {{- end }} - {{- if and .app.harness.database (kindIs "map" .app.harness.database) (hasKey .app.harness.database "external_connect_string") .app.harness.database.external_connect_string }} + {{- if kindIs "map" .app.harness.database }} + {{- if and (hasKey .app.harness.database "external_connect_string") .app.harness.database.external_connect_string }} - name: db-external mountPath: "/opt/cloudharness/resources/db" readOnly: true + {{- end }} {{- end }} volumes: - name: cloudharness-allvalues @@ -174,10 +176,12 @@ spec: secret: secretName: {{ .app.harness.deployment.name }} {{- end }} - {{- if and .app.harness.database (kindIs "map" .app.harness.database) (hasKey .app.harness.database "external_connect_string") .app.harness.database.external_connect_string }} + {{- if kindIs "map" .app.harness.database }} + {{- if and (hasKey .app.harness.database "external_connect_string") .app.harness.database.external_connect_string }} - name: db-external secret: secretName: {{ printf "%s-db" .app.harness.deployment.name }} + {{- end }} {{- end }} --- {{- end }} diff --git a/deployment-configuration/helm/templates/auto-secrets.yaml b/deployment-configuration/helm/templates/auto-secrets.yaml index 8cad6353..df34fc92 100644 --- a/deployment-configuration/helm/templates/auto-secrets.yaml +++ b/deployment-configuration/helm/templates/auto-secrets.yaml @@ -73,7 +73,9 @@ stringData: --- {{- end }} {{- range $app := .Values.apps }} - {{- if and $app.harness.database (kindIs "map" $app.harness.database) (hasKey $app.harness.database "external_connect_string") $app.harness.database.external_connect_string }} + {{- if kindIs "map" $app.harness.database }} + {{- if and (hasKey $app.harness.database "external_connect_string") $app.harness.database.external_connect_string }} {{- include "deploy_utils.db_external_secret" (dict "root" $ "app" $app) }} + {{- end }} {{- end }} {{- end }} \ No newline at end of file diff --git a/deployment-configuration/helm/templates/configmap.yaml b/deployment-configuration/helm/templates/configmap.yaml index c6db7693..74869fc7 100644 --- a/deployment-configuration/helm/templates/configmap.yaml +++ b/deployment-configuration/helm/templates/configmap.yaml @@ -20,7 +20,7 @@ data: {{- range $key, $val := .Values.apps }} {{- $app := get $values_copy.apps $key }} {{- $tmp := set $app.harness "secrets" dict }} - {{- if and $app.harness.database (kindIs "map" $app.harness.database) }} + {{- if kindIs "map" $app.harness.database }} {{- $tmp := unset $app.harness.database "external_connect_string" }} {{- end }} {{- end }} From 71b1f26b3a8acb8cb6acbb8d8768f581764cddfb Mon Sep 17 00:00:00 2001 From: Filippo Ledda Date: Mon, 9 Mar 2026 21:38:14 +0100 Subject: [PATCH 24/33] CH-243 ingress root path fix --- deployment-configuration/helm/templates/ingress.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment-configuration/helm/templates/ingress.yaml b/deployment-configuration/helm/templates/ingress.yaml index f79b2d61..0416cfd3 100644 --- a/deployment-configuration/helm/templates/ingress.yaml +++ b/deployment-configuration/helm/templates/ingress.yaml @@ -12,7 +12,7 @@ {{- $uri := $mapping.uri }} {{- if eq $uri "/" }} - path: / - pathType: {{ $app.harness.gateway.pathType | default $root.Values.ingress.pathType | default "Prefix" }} + pathType: Exact backend: service: name: {{ $app.harness.service.name | quote }} From 5274ec8c51de13980997db21a75a17505cdc21f5 Mon Sep 17 00:00:00 2001 From: Filippo Ledda Date: Tue, 10 Mar 2026 10:31:25 +0100 Subject: [PATCH 25/33] chore: linting and test related fixes --- applications/samples/api/openapi.yaml | 5 +-- .../samples/backend/.openapi-generator-ignore | 2 +- .../controllers/security_controller.py | 32 +++++++++++++++++++ .../backend/samples/models/__init__.py | 1 - .../backend/samples/openapi/openapi.yaml | 11 +------ .../myapp/deploy/values-withoutdb.yaml | 4 +++ 6 files changed, 39 insertions(+), 16 deletions(-) create mode 100644 applications/samples/backend/samples/controllers/security_controller.py diff --git a/applications/samples/api/openapi.yaml b/applications/samples/api/openapi.yaml index 24c9a026..ed4adc91 100644 --- a/applications/samples/api/openapi.yaml +++ b/applications/samples/api/openapi.yaml @@ -96,10 +96,7 @@ paths: content: application/json: schema: - type: object - properties: - connect_string: - type: string + type: string "500": description: Error retrieving database connection string /sampleresources: diff --git a/applications/samples/backend/.openapi-generator-ignore b/applications/samples/backend/.openapi-generator-ignore index 187605aa..c89f830d 100644 --- a/applications/samples/backend/.openapi-generator-ignore +++ b/applications/samples/backend/.openapi-generator-ignore @@ -22,7 +22,7 @@ # Then explicitly reverse the ignore rule for a single file: #!docs/README.md setup.py -# */controllers/* +*/controllers/* Dockerfile */__main__.py */test/* diff --git a/applications/samples/backend/samples/controllers/security_controller.py b/applications/samples/backend/samples/controllers/security_controller.py new file mode 100644 index 00000000..c602c2d2 --- /dev/null +++ b/applications/samples/backend/samples/controllers/security_controller.py @@ -0,0 +1,32 @@ +from typing import List + + +def info_from_bearerAuth(token): + """ + Check and retrieve authentication information from custom bearer token. + Returned value will be passed in 'token_info' parameter of your operation function, if there is one. + 'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one. + + :param token Token provided by Authorization header + :type token: str + :return: Decoded token information or None if token is invalid + :rtype: dict | None + """ + return {'uid': 'user_id'} + + +def info_from_cookieAuth(api_key, required_scopes): + """ + Check and retrieve authentication information from api_key. + Returned value will be passed in 'token_info' parameter of your operation function, if there is one. + 'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one. + + :param api_key API key provided by Authorization header + :type api_key: str + :param required_scopes Always None. Used for other authentication method + :type required_scopes: None + :return: Information attached to provided api_key or None if api_key is invalid or does not allow access to called API + :rtype: dict | None + """ + return {'uid': 'user_id'} + diff --git a/applications/samples/backend/samples/models/__init__.py b/applications/samples/backend/samples/models/__init__.py index e2138631..94617398 100644 --- a/applications/samples/backend/samples/models/__init__.py +++ b/applications/samples/backend/samples/models/__init__.py @@ -1,6 +1,5 @@ # flake8: noqa # import models into model package -from samples.models.get_db_connect_string200_response import GetDbConnectString200Response from samples.models.inline_response202 import InlineResponse202 from samples.models.inline_response202_task import InlineResponse202Task from samples.models.sample_resource import SampleResource diff --git a/applications/samples/backend/samples/openapi/openapi.yaml b/applications/samples/backend/samples/openapi/openapi.yaml index ef83e8be..3bf1d1c5 100644 --- a/applications/samples/backend/samples/openapi/openapi.yaml +++ b/applications/samples/backend/samples/openapi/openapi.yaml @@ -24,7 +24,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/get_db_connect_string_200_response' + type: string description: Database connection string returned successfully "500": description: Error retrieving database connection string @@ -343,15 +343,6 @@ components: - a title: SampleResource type: object - get_db_connect_string_200_response: - example: - connect_string: connect_string - properties: - connect_string: - title: connect_string - type: string - title: get_db_connect_string_200_response - type: object securitySchemes: bearerAuth: bearerFormat: JWT diff --git a/tools/deployment-cli-tools/tests/resources/applications/myapp/deploy/values-withoutdb.yaml b/tools/deployment-cli-tools/tests/resources/applications/myapp/deploy/values-withoutdb.yaml index e69de29b..0c56e1d0 100644 --- a/tools/deployment-cli-tools/tests/resources/applications/myapp/deploy/values-withoutdb.yaml +++ b/tools/deployment-cli-tools/tests/resources/applications/myapp/deploy/values-withoutdb.yaml @@ -0,0 +1,4 @@ +harness: + database: + auto: false + type: \ No newline at end of file From d8cb7fe30547bf328718e049ca30931f1c05d958 Mon Sep 17 00:00:00 2001 From: Filippo Ledda Date: Tue, 10 Mar 2026 10:51:29 +0100 Subject: [PATCH 26/33] chore: Python code linting fix --- .../backend/samples/controllers/security_controller.py | 1 - applications/samples/backend/samples/util.py | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/applications/samples/backend/samples/controllers/security_controller.py b/applications/samples/backend/samples/controllers/security_controller.py index c602c2d2..25258489 100644 --- a/applications/samples/backend/samples/controllers/security_controller.py +++ b/applications/samples/backend/samples/controllers/security_controller.py @@ -29,4 +29,3 @@ def info_from_cookieAuth(api_key, required_scopes): :rtype: dict | None """ return {'uid': 'user_id'} - diff --git a/applications/samples/backend/samples/util.py b/applications/samples/backend/samples/util.py index 5b241814..b802fafd 100644 --- a/applications/samples/backend/samples/util.py +++ b/applications/samples/backend/samples/util.py @@ -67,8 +67,8 @@ def deserialize_date(string): :rtype: date """ if string is None: - return None - + return None + try: from dateutil.parser import parse return parse(string).date() @@ -87,8 +87,8 @@ def deserialize_datetime(string): :rtype: datetime """ if string is None: - return None - + return None + try: from dateutil.parser import parse return parse(string) @@ -144,4 +144,4 @@ def _deserialize_dict(data, boxed_type): :rtype: dict """ return {k: _deserialize(v, boxed_type) - for k, v in data.items() } + for k, v in data.items()} From cc5b12570269eecfaea2c3ffe0c13a05a3991fb0 Mon Sep 17 00:00:00 2001 From: Filippo Ledda Date: Tue, 10 Mar 2026 11:28:02 +0100 Subject: [PATCH 27/33] chore: Update docs and default sample configuration --- applications/samples/deploy/values.yaml | 4 ++- docs/ingress-domains-proxies.md | 36 +++++++++++++++---------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/applications/samples/deploy/values.yaml b/applications/samples/deploy/values.yaml index 54da46a7..6b84ed43 100644 --- a/applications/samples/deploy/values.yaml +++ b/applications/samples/deploy/values.yaml @@ -7,7 +7,6 @@ harness: service: port: 8080 auto: true - route_pattern: '/(?!\/metrics\/?$)\/.*' use_services: - name: common deployment: @@ -19,6 +18,9 @@ harness: usenfs: false auto: true port: 8080 + gateway: + path: / + pathType: Prefix proxy: gatekeeper: replicas: 1 diff --git a/docs/ingress-domains-proxies.md b/docs/ingress-domains-proxies.md index 349ac53f..178e6465 100644 --- a/docs/ingress-domains-proxies.md +++ b/docs/ingress-domains-proxies.md @@ -68,25 +68,35 @@ proxy: 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). +See also the [gatekeepers documentation](./accounts.md#secure-and-endpoint-with-the-gatekeeper). ## Route pattern configuration Cloud Harness allows customizing which request paths are routed to an application -via a glob-style regular expression. There are two levels where this can be set: +via paths and regular expression. There are two levels where this can be set: -- **Global**: `ingress.route_pattern` in `deployment-configuration/helm/values.yaml` (applies to all apps by default). -- **Application**: `harness.route_pattern` in an application's `values.yaml` (overrides the global value for that app). +- **Global**: `ingress.path` and `ingress.pathType` in `deployment-configuration/helm/values.yaml` (applies to all apps by default). +- **Application**: `harness.gateway.path` and `harness.gateway.pathType` in an application's `values.yaml` (overrides the global value for that app). -The Helm ingress template uses the application-level `harness.route_pattern` when present, -falling back to the global `ingress.route_pattern` otherwise. +The Helm ingress template uses the application-level `harness.gateway` when present, +falling back to the global `ingress` otherwise. -Example (global default in `deployment-configuration/helm/values.yaml`): +The default configuration uses Prefix paths for the highest compatibility: + +```yaml +path: / +pathType: Prefix +``` +The default configuration will work for a single application being served in the root directory within the domain with no exclusions. + + +Example with regular expression (global default in `deployment-configuration/helm/values.yaml`): ```yaml ingress: - # Default regex segment for routes (used in paths like '/(pattern)') - route_pattern: "/(.*)" + # Example regex segment for routes (used in paths like '/(pattern)') + path: "/(.*)" + pathType: ImplementationSpecific ``` Example (application override in `applications//deploy/values.yaml`): @@ -94,12 +104,10 @@ Example (application override in `applications//deploy/values.yaml`): ```yaml harness: # route_pattern is used to build the Ingress path for the app - route_pattern: '/((?!(?:metrics)(?:/)?$).*)' # exclude only '/metrics' and '/metrics/' + path: '/((?!(?:metrics)(?:/)?$).*)' # exclude only '/metrics' and '/metrics/' + pathType: ImplementationSpecific ``` -Notes: +Customization notes: - The pattern is inserted into the generated Ingress `path` field. Make sure the regex is valid for your ingress controller and matches the expected path syntax. -- If you only need to exclude a single exact path (for example `/metrics`), use a - negative lookahead like the example above. If you prefer a simpler global default, - leave `ingress.route_pattern` as `"/(.*)"` and override per-app when needed. From e94a771a842f41274a8d71b1d6ffee23568ba3b5 Mon Sep 17 00:00:00 2001 From: Alex Burdusel Date: Tue, 10 Mar 2026 15:01:18 +0200 Subject: [PATCH 28/33] Update CH default image version to 4.6.0 --- .../compose/templates/auto-gatekeepers.yaml | 2 +- deployment-configuration/helm/values.yaml | 2 +- deployment-configuration/value-template.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/deployment-configuration/compose/templates/auto-gatekeepers.yaml b/deployment-configuration/compose/templates/auto-gatekeepers.yaml index 730f5bd0..4b78ec86 100644 --- a/deployment-configuration/compose/templates/auto-gatekeepers.yaml +++ b/deployment-configuration/compose/templates/auto-gatekeepers.yaml @@ -6,7 +6,7 @@ networks: - ch restart: always - image: quay.io/gogatekeeper/gatekeeper:2.14.3 + image: quay.io/gogatekeeper/gatekeeper:4.6.0 expose: - '8080' - '8443' diff --git a/deployment-configuration/helm/values.yaml b/deployment-configuration/helm/values.yaml index c7dd85ae..fe752226 100644 --- a/deployment-configuration/helm/values.yaml +++ b/deployment-configuration/helm/values.yaml @@ -90,7 +90,7 @@ proxy: max: 250 gatekeeper: # -- Default gatekeeper image - image: "quay.io/gogatekeeper/gatekeeper:2.14.3" + image: "quay.io/gogatekeeper/gatekeeper:4.6.0" # -- Default number of gatekeeper replicas replicas: 1 secret: "" diff --git a/deployment-configuration/value-template.yaml b/deployment-configuration/value-template.yaml index a7444df5..93d7ab94 100644 --- a/deployment-configuration/value-template.yaml +++ b/deployment-configuration/value-template.yaml @@ -142,7 +142,7 @@ harness: max: gatekeeper: # -- Default gatekeeper image - image: "quay.io/gogatekeeper/gatekeeper:2.14.3" + image: "quay.io/gogatekeeper/gatekeeper:4.6.0" # -- Default number of gatekeeper replicas replicas: 1 resources: From 244e2066919f90a7227f342627e98a9382ad6aaa Mon Sep 17 00:00:00 2001 From: Filippo Ledda Date: Tue, 10 Mar 2026 19:05:51 +0100 Subject: [PATCH 29/33] CH-248 #840 chore: rename connect string variable --- .github/instructions/copilot-instructions.md | 8 +++++++- applications/samples/deploy/values.yaml | 2 +- .../helm/templates/auto-deployments.yaml | 4 ++-- .../helm/templates/auto-secrets.yaml | 8 ++++---- deployment-configuration/helm/templates/configmap.yaml | 2 +- deployment-configuration/value-template.yaml | 2 +- docs/model/DatabaseDeploymentConfig.md | 2 +- .../cloudharness-common/cloudharness/applications.py | 6 +++--- libraries/models/api/openapi.yaml | 2 +- .../models/database_deployment_config.py | 6 +++--- libraries/models/docs/DatabaseDeploymentConfig.md | 2 +- tools/deployment-cli-tools/ch_cli_tools/codefresh.py | 8 ++++---- .../myapp/deploy/values-connectstring.yaml | 2 ++ tools/deployment-cli-tools/tests/test_codefresh.py | 10 +++++----- 14 files changed, 36 insertions(+), 28 deletions(-) create mode 100644 tools/deployment-cli-tools/tests/resources/applications/myapp/deploy/values-connectstring.yaml diff --git a/.github/instructions/copilot-instructions.md b/.github/instructions/copilot-instructions.md index b54a1632..5ba106c3 100644 --- a/.github/instructions/copilot-instructions.md +++ b/.github/instructions/copilot-instructions.md @@ -20,4 +20,10 @@ Verify which application/components is in scope and read specific prompt instruc Check best practices in every instruction file in scope and docs and apply them when writing code or performing code reviews. Use reference for any questions regarding project structure, development workflow, and best practices. -If you have any doubts about where to find information, ask for clarification before proceeding. \ No newline at end of file +If you have any doubts about where to find information, ask for clarification before proceeding. + +### Development principles +- Follow the best practices and coding style guidelines outlined in the documentation and instruction files. +- Configuration is set on values.yaml files and injected into the application via Helm templates and Kubernetes manifests. Do not hardcode configuration values directly into the application code or templates. +- Structured configuration can be injected via resources, that are process by helm templates and loaded as ConfigMaps automatically. See for instance `applications/accounts/deploy/resources/realm.json` +- The cloud harness configuration API is handled by the models library and defined as [openapi spec](../../libraries/models/api/openapi.yaml). Use `harness-generate models` to generate the models library after making changes to the spec. \ No newline at end of file diff --git a/applications/samples/deploy/values.yaml b/applications/samples/deploy/values.yaml index 6b84ed43..b5601aba 100644 --- a/applications/samples/deploy/values.yaml +++ b/applications/samples/deploy/values.yaml @@ -102,4 +102,4 @@ harness: TEST_ARGUMENT: example value database: type: postgres - external_connect_string: "test connection string" \ No newline at end of file + connect_string: "test connection string" \ No newline at end of file diff --git a/deployment-configuration/helm/templates/auto-deployments.yaml b/deployment-configuration/helm/templates/auto-deployments.yaml index 30bb9083..efa125d5 100644 --- a/deployment-configuration/helm/templates/auto-deployments.yaml +++ b/deployment-configuration/helm/templates/auto-deployments.yaml @@ -141,7 +141,7 @@ spec: readOnly: true {{- end }} {{- if kindIs "map" .app.harness.database }} - {{- if and (hasKey .app.harness.database "external_connect_string") .app.harness.database.external_connect_string }} + {{- if and (hasKey .app.harness.database "connect_string") .app.harness.database.connect_string }} - name: db-external mountPath: "/opt/cloudharness/resources/db" readOnly: true @@ -177,7 +177,7 @@ spec: secretName: {{ .app.harness.deployment.name }} {{- end }} {{- if kindIs "map" .app.harness.database }} - {{- if and (hasKey .app.harness.database "external_connect_string") .app.harness.database.external_connect_string }} + {{- if and (hasKey .app.harness.database "connect_string") .app.harness.database.connect_string }} - name: db-external secret: secretName: {{ printf "%s-db" .app.harness.deployment.name }} diff --git a/deployment-configuration/helm/templates/auto-secrets.yaml b/deployment-configuration/helm/templates/auto-secrets.yaml index df34fc92..ad17d1e6 100644 --- a/deployment-configuration/helm/templates/auto-secrets.yaml +++ b/deployment-configuration/helm/templates/auto-secrets.yaml @@ -58,7 +58,7 @@ stringData: {{- end }} {{- end }} {{- end }} -{{- define "deploy_utils.db_external_secret" }} +{{- define "deploy_utils.db_secret" }} {{- $secret_name := printf "%s-db" .app.harness.deployment.name }} apiVersion: v1 kind: Secret @@ -69,13 +69,13 @@ metadata: app: {{ .app.harness.deployment.name }} type: Opaque stringData: - external_connect_string: {{ .app.harness.database.external_connect_string | quote }} + connect_string: {{ .app.harness.database.connect_string | quote }} --- {{- end }} {{- range $app := .Values.apps }} {{- if kindIs "map" $app.harness.database }} - {{- if and (hasKey $app.harness.database "external_connect_string") $app.harness.database.external_connect_string }} - {{- include "deploy_utils.db_external_secret" (dict "root" $ "app" $app) }} + {{- if and (hasKey $app.harness.database "connect_string") $app.harness.database.connect_string }} + {{- include "deploy_utils.db_secret" (dict "root" $ "app" $app) }} {{- end }} {{- end }} {{- end }} \ No newline at end of file diff --git a/deployment-configuration/helm/templates/configmap.yaml b/deployment-configuration/helm/templates/configmap.yaml index 74869fc7..d061a935 100644 --- a/deployment-configuration/helm/templates/configmap.yaml +++ b/deployment-configuration/helm/templates/configmap.yaml @@ -21,7 +21,7 @@ data: {{- $app := get $values_copy.apps $key }} {{- $tmp := set $app.harness "secrets" dict }} {{- if kindIs "map" $app.harness.database }} - {{- $tmp := unset $app.harness.database "external_connect_string" }} + {{- $tmp := unset $app.harness.database "connect_string" }} {{- end }} {{- end }} {{ $values_copy | toYaml | indent 4 }} \ No newline at end of file diff --git a/deployment-configuration/value-template.yaml b/deployment-configuration/value-template.yaml index a7444df5..f90727fe 100644 --- a/deployment-configuration/value-template.yaml +++ b/deployment-configuration/value-template.yaml @@ -78,7 +78,7 @@ harness: # -- expose database to the public with ingress expose: false # -- Set to "" to set the set the string in the CI/CD as a secret. Only set the full value for dev/testing - external_connect_string: + connect_string: # -- settings for mongo database (for type==mongo) mongo: image: mongo:5 diff --git a/docs/model/DatabaseDeploymentConfig.md b/docs/model/DatabaseDeploymentConfig.md index c4924aab..143a76cb 100644 --- a/docs/model/DatabaseDeploymentConfig.md +++ b/docs/model/DatabaseDeploymentConfig.md @@ -17,7 +17,7 @@ Name | Type | Description | Notes **postgres** | **Dict[str, object]** | | [optional] **neo4j** | **object** | Neo4j database specific configuration | [optional] **resources** | [**DeploymentResourcesConf**](DeploymentResourcesConf.md) | | [optional] -**external_connect_string** | **str** | Specify if the database is external. If not null, auto deployment if set will not be used. Leave it as an empty string and the connect string will be provided as a secret to be provided at CI/CD (recommended) | [optional] +**connect_string** | **str** | Specify if the database is external. If not null, auto deployment if set will not be used. Leave it as an empty string and the connect string will be provided as a secret to be provided at CI/CD (recommended) | [optional] ## Example diff --git a/libraries/cloudharness-common/cloudharness/applications.py b/libraries/cloudharness-common/cloudharness/applications.py index 86c8867d..667e93e6 100644 --- a/libraries/cloudharness-common/cloudharness/applications.py +++ b/libraries/cloudharness-common/cloudharness/applications.py @@ -37,9 +37,9 @@ def is_sentry_enabled(self) -> bool: return self.harness.sentry def get_db_connection_string(self, **kwargs) -> str: - external_connect_string_path = "/opt/cloudharness/resources/db/external_connect_string" - if os.path.isfile(external_connect_string_path): - with open(external_connect_string_path) as f: + connect_string_path = "/opt/cloudharness/resources/db/connect_string" + if os.path.isfile(connect_string_path): + with open(connect_string_path) as f: return f.read().strip() if not self.is_auto_db(): raise ConfigurationCallException( diff --git a/libraries/models/api/openapi.yaml b/libraries/models/api/openapi.yaml index f3e27735..b2e4e559 100644 --- a/libraries/models/api/openapi.yaml +++ b/libraries/models/api/openapi.yaml @@ -654,7 +654,7 @@ components: resources: $ref: '#/components/schemas/DeploymentResourcesConf' description: Database deployment resources - external_connect_string: + connect_string: description: >- Specify if the database is external. If not null, auto deployment if set will not diff --git a/libraries/models/cloudharness_model/models/database_deployment_config.py b/libraries/models/cloudharness_model/models/database_deployment_config.py index 04767742..c059557e 100644 --- a/libraries/models/cloudharness_model/models/database_deployment_config.py +++ b/libraries/models/cloudharness_model/models/database_deployment_config.py @@ -42,9 +42,9 @@ class DatabaseDeploymentConfig(CloudHarnessBaseModel): postgres: Optional[Dict[str, Any]] = None neo4j: Optional[Any] = Field(default=None, description="Neo4j database specific configuration") resources: Optional[DeploymentResourcesConf] = None - external_connect_string: Optional[StrictStr] = Field(default=None, description="Specify if the database is external. If not null, auto deployment if set will not be used. Leva it as an empty string and the connect string will be provided as a secret to be provided at CI/CD (recommended)") + connect_string: Optional[StrictStr] = Field(default=None, description="Specify if the database is external. If not null, auto deployment if set will not be used. Leva it as an empty string and the connect string will be provided as a secret to be provided at CI/CD (recommended)") additional_properties: Dict[str, Any] = {} - __properties: ClassVar[List[str]] = ["auto", "name", "type", "size", "user", "pass", "image_ref", "mongo", "postgres", "neo4j", "resources", "external_connect_string"] + __properties: ClassVar[List[str]] = ["auto", "name", "type", "size", "user", "pass", "image_ref", "mongo", "postgres", "neo4j", "resources", "connect_string"] @field_validator('type') def type_validate_regular_expression(cls, value): @@ -112,7 +112,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "postgres": obj.get("postgres"), "neo4j": obj.get("neo4j"), "resources": DeploymentResourcesConf.from_dict(obj["resources"]) if obj.get("resources") is not None else None, - "external_connect_string": obj.get("external_connect_string") + "connect_string": obj.get("connect_string") }) # store additional fields in additional_properties for _key in obj.keys(): diff --git a/libraries/models/docs/DatabaseDeploymentConfig.md b/libraries/models/docs/DatabaseDeploymentConfig.md index f08b83fe..9babd843 100644 --- a/libraries/models/docs/DatabaseDeploymentConfig.md +++ b/libraries/models/docs/DatabaseDeploymentConfig.md @@ -17,7 +17,7 @@ Name | Type | Description | Notes **postgres** | **Dict[str, object]** | | [optional] **neo4j** | **object** | Neo4j database specific configuration | [optional] **resources** | [**DeploymentResourcesConf**](DeploymentResourcesConf.md) | | [optional] -**external_connect_string** | **str** | Specify if the database is external. If not null, auto deployment if set will not be used. Leva it as an empty string and the connect string will be provided as a secret to be provided at CI/CD (recommended) | [optional] +**connect_string** | **str** | Specify if the database is external. If not null, auto deployment if set will not be used. Leva it as an empty string and the connect string will be provided as a secret to be provided at CI/CD (recommended) | [optional] ## Example diff --git a/tools/deployment-cli-tools/ch_cli_tools/codefresh.py b/tools/deployment-cli-tools/ch_cli_tools/codefresh.py index 44e11779..691cf40d 100644 --- a/tools/deployment-cli-tools/ch_cli_tools/codefresh.py +++ b/tools/deployment-cli-tools/ch_cli_tools/codefresh.py @@ -400,12 +400,12 @@ def adjust_build_steps(index): secret_name = secret.replace("_", "__") arguments["custom_values"].append( "apps_%s_harness_secrets_%s=${{%s}}" % (app_name.replace("_", "__"), secret_name, secret_name.upper())) - # Add external_connect_string as a secret custom_value for apps that have it set to empty + # Add connect_string as a secret custom_value for apps that have it set to empty for app_name, app in helm_values.apps.items(): - if app.harness.database and app.harness.database.get("external_connect_string") == "": - var_name = f"{app_name.upper().replace('-', '_')}_DB_EXTERNAL_CONNECT_STRING" + if app.harness.database and app.harness.database.get("connect_string") == "": + var_name = f"{app_name.upper().replace('-', '_')}_DB_CONNECT_STRING" arguments["custom_values"].append( - "apps_%s_harness_database_external__connect__string=${{%s}}" % ( + "apps_%s_harness_database_connect__string=${{%s}}" % ( app_name.replace("_", "__"), var_name) ) # Add registry secret value secret if registry secret name is set diff --git a/tools/deployment-cli-tools/tests/resources/applications/myapp/deploy/values-connectstring.yaml b/tools/deployment-cli-tools/tests/resources/applications/myapp/deploy/values-connectstring.yaml new file mode 100644 index 00000000..ec41dd3f --- /dev/null +++ b/tools/deployment-cli-tools/tests/resources/applications/myapp/deploy/values-connectstring.yaml @@ -0,0 +1,2 @@ +harness: + database: {connect_string: "", type: postgres} \ No newline at end of file diff --git a/tools/deployment-cli-tools/tests/test_codefresh.py b/tools/deployment-cli-tools/tests/test_codefresh.py index ee9f158d..4ce13aef 100644 --- a/tools/deployment-cli-tools/tests/test_codefresh.py +++ b/tools/deployment-cli-tools/tests/test_codefresh.py @@ -331,8 +331,8 @@ def test_create_codefresh_configuration_nobuild(): assert "publish_myapp-mytask" in l1_steps["publish"]["steps"] -def test_codefresh_db_external_connect_string_secret(): - """When an app has database.external_connect_string set to '', a custom_values entry must be added to the deployment step.""" +def test_codefresh_db_connect_string_secret(): + """When an app has database.connect_string set to '', a custom_values entry must be added to the deployment step.""" values = create_helm_chart( [CLOUDHARNESS_ROOT, RESOURCES], output_path=OUT, @@ -340,7 +340,7 @@ def test_codefresh_db_external_connect_string_secret(): exclude=['events'], domain="my.local", namespace='test', - env='dev', + env='connectstring', local=False, tag=1, registry='reg' @@ -358,9 +358,9 @@ def test_codefresh_db_external_connect_string_secret(): base_image_name=values['name'], helm_values=values, save=False) custom_values = cf['steps']['deployment']['arguments']['custom_values'] - expected = "apps_myapp_harness_database_external__connect__string=${{MYAPP_DB_EXTERNAL_CONNECT_STRING}}" + expected = "apps_myapp_harness_database_connect__string=${{MYAPP_DB_CONNECT_STRING}}" assert expected in custom_values, \ - f"Expected custom_value entry for external_connect_string not found. Got: {custom_values}" + f"Expected custom_value entry for connect_string not found. Got: {custom_values}" finally: shutil.rmtree(BUILD_MERGE_DIR, ignore_errors=True) From eee05b161c49696339fcdf8603926fd20b30ad5d Mon Sep 17 00:00:00 2001 From: Filippo Ledda Date: Wed, 11 Mar 2026 16:30:27 +0100 Subject: [PATCH 30/33] add django command to sync users --- .../accounts/deploy/resources/realm.json | 1 + applications/accounts/deploy/values.yaml | 6 ++-- .../management/__init__.py | 0 .../management/commands/__init__.py | 0 .../management/commands/cloudharness.py | 29 +++++++++++++++++++ 5 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 infrastructure/common-images/cloudharness-django/libraries/cloudharness-django/cloudharness_django/management/__init__.py create mode 100644 infrastructure/common-images/cloudharness-django/libraries/cloudharness-django/cloudharness_django/management/commands/__init__.py create mode 100644 infrastructure/common-images/cloudharness-django/libraries/cloudharness-django/cloudharness_django/management/commands/cloudharness.py diff --git a/applications/accounts/deploy/resources/realm.json b/applications/accounts/deploy/resources/realm.json index cd280f02..51d1318d 100644 --- a/applications/accounts/deploy/resources/realm.json +++ b/applications/accounts/deploy/resources/realm.json @@ -12,6 +12,7 @@ "rememberMe": true, "verifyEmail": false, "loginWithEmailAllowed": true, + "organizationsEnabled": {{ .Values.apps.accounts.realm.organizationsEnabled | default false }}, "duplicateEmailsAllowed": false, "resetPasswordAllowed": true, "editUsernameAllowed": {{ .Values.apps.accounts.editUsernameAllowed }}, diff --git a/applications/accounts/deploy/values.yaml b/applications/accounts/deploy/values.yaml index 672601a2..6140e0bd 100644 --- a/applications/accounts/deploy/values.yaml +++ b/applications/accounts/deploy/values.yaml @@ -87,10 +87,12 @@ admin: editUsernameAllowed: true useEvents: true identityProviders: -- github -- google + - github + - google theme: login: "keycloak" account: "keycloak" admin: "keycloak" email: "keycloak" +realm: + organizationsEnabled: false diff --git a/infrastructure/common-images/cloudharness-django/libraries/cloudharness-django/cloudharness_django/management/__init__.py b/infrastructure/common-images/cloudharness-django/libraries/cloudharness-django/cloudharness_django/management/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/infrastructure/common-images/cloudharness-django/libraries/cloudharness-django/cloudharness_django/management/commands/__init__.py b/infrastructure/common-images/cloudharness-django/libraries/cloudharness-django/cloudharness_django/management/commands/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/infrastructure/common-images/cloudharness-django/libraries/cloudharness-django/cloudharness_django/management/commands/cloudharness.py b/infrastructure/common-images/cloudharness-django/libraries/cloudharness-django/cloudharness_django/management/commands/cloudharness.py new file mode 100644 index 00000000..d4568390 --- /dev/null +++ b/infrastructure/common-images/cloudharness-django/libraries/cloudharness-django/cloudharness_django/management/commands/cloudharness.py @@ -0,0 +1,29 @@ +from django.core.management.base import BaseCommand, CommandError +from cloudharness_django.services import get_user_service + + +class Command(BaseCommand): + help = 'CloudHarness management commands' + + def add_arguments(self, parser): + parser.add_argument( + 'subcommand', + type=str, + help='Subcommand to run (e.g., sync)', + ) + + def handle(self, *args, **options): + subcommand = options.get('subcommand') + + if subcommand == 'sync': + self.sync_keycloak() + else: + raise CommandError(f'Unknown subcommand: {subcommand}') + + def sync_keycloak(self): + """Sync Keycloak users and groups.""" + try: + get_user_service().sync_kc_users_groups() + self.stdout.write(self.style.SUCCESS('Keycloak users & groups synced successfully.')) + except Exception as e: + raise CommandError(f'Failed to sync Keycloak users & groups: {str(e)}') From 9550a74cbe36d12a1f6a36c468063a82902f57dc Mon Sep 17 00:00:00 2001 From: Filippo Ledda Date: Wed, 11 Mar 2026 19:24:43 +0100 Subject: [PATCH 31/33] CH-250 alternative dockerfiles implementation --- applications/samples/test.Dockerfile | 35 ++++++++ .../ch_cli_tools/codefresh.py | 13 ++- .../ch_cli_tools/skaffold.py | 13 ++- tools/deployment-cli-tools/harness-deployment | 2 +- .../applications/myapp/dev.Dockerfile | 3 + .../tests/test_codefresh.py | 81 ++++++++++++++++- .../tests/test_skaffold.py | 86 +++++++++++++++++++ 7 files changed, 227 insertions(+), 6 deletions(-) create mode 100644 applications/samples/test.Dockerfile create mode 100644 tools/deployment-cli-tools/tests/resources/applications/myapp/dev.Dockerfile diff --git a/applications/samples/test.Dockerfile b/applications/samples/test.Dockerfile new file mode 100644 index 00000000..a0b9f8c8 --- /dev/null +++ b/applications/samples/test.Dockerfile @@ -0,0 +1,35 @@ +ARG CLOUDHARNESS_FRONTEND_BUILD +ARG CLOUDHARNESS_FLASK + +FROM $CLOUDHARNESS_FRONTEND_BUILD as frontend + +ARG TEST_ARGUMENT=default +RUN echo $TEST_ARGUMENT + +ENV APP_DIR=/app + +WORKDIR ${APP_DIR} +COPY frontend/package.json ${APP_DIR} +COPY frontend/yarn.lock ${APP_DIR} +RUN yarn install --frozen-lockfile --timeout 60000 + +COPY frontend ${APP_DIR} +RUN yarn build + +##### +FROM $CLOUDHARNESS_FLASK +ENV MODULE_NAME=samples + +ENV WORKERS=2 +ENV PORT=8080 + +COPY backend/requirements.txt /usr/src/app/ + +RUN --mount=type=cache,target=/root/.cache python -m pip install --upgrade pip &&\ + pip3 install -r requirements.txt --prefer-binary + +COPY backend/ /usr/src/app + +COPY --from=frontend app/dist/ /usr/src/app/www + +RUN pip3 install -e . diff --git a/tools/deployment-cli-tools/ch_cli_tools/codefresh.py b/tools/deployment-cli-tools/ch_cli_tools/codefresh.py index 691cf40d..631ed9d8 100644 --- a/tools/deployment-cli-tools/ch_cli_tools/codefresh.py +++ b/tools/deployment-cli-tools/ch_cli_tools/codefresh.py @@ -219,6 +219,14 @@ def add_arg_dependencies(dependencies): build["when"] = when_condition return build + def resolve_dockerfile_name(dockerfile_dir): + """Return the dockerfile filename, preferring [env].Dockerfile over Dockerfile.""" + for env_name in envs: + env_dockerfile = os.path.join(dockerfile_dir, f'{env_name}.Dockerfile') + if exists(env_dockerfile): + return f'{env_name}.Dockerfile' + return 'Dockerfile' + def codefresh_steps_from_base_path(base_path, fixed_context=None, include=build_included, publish=True): found = False for dockerfile_path in find_dockerfiles_paths(base_path): @@ -251,8 +259,9 @@ def codefresh_steps_from_base_path(base_path, fixed_context=None, include=build_ build = None if CD_BUILD_STEP_PARALLEL in steps: + dockerfile_name = resolve_dockerfile_name(dockerfile_path) dependencies = guess_build_dependencies_from_dockerfile( - join(dockerfile_path, "Dockerfile") + join(dockerfile_path, dockerfile_name) ) build = codefresh_app_build_spec( app_name=app_name, @@ -262,7 +271,7 @@ def codefresh_steps_from_base_path(base_path, fixed_context=None, include=build_ dockerfile_path=join( relpath( dockerfile_path, root_path) if fixed_context else '', - "Dockerfile"), + dockerfile_name), helm_values=helm_values, dependencies=dependencies, additional_tags=('latest',) if not publish else () diff --git a/tools/deployment-cli-tools/ch_cli_tools/skaffold.py b/tools/deployment-cli-tools/ch_cli_tools/skaffold.py index 43f47f5c..0d19c2ab 100644 --- a/tools/deployment-cli-tools/ch_cli_tools/skaffold.py +++ b/tools/deployment-cli-tools/ch_cli_tools/skaffold.py @@ -29,7 +29,7 @@ def get_all_images(helm_values: HarnessMainConfig) -> dict[str, str]: return all_images -def create_skaffold_configuration(root_paths, helm_values: HarnessMainConfig, output_path='.', manage_task_images=True, backend_deploy=HELM_ENGINE): +def create_skaffold_configuration(root_paths, helm_values: HarnessMainConfig, output_path='.', manage_task_images=True, backend_deploy=HELM_ENGINE, env=None): backend = backend_deploy or HELM_ENGINE template_name = 'skaffold-template.yaml' skaffold_conf = get_template(template_name, True) @@ -45,6 +45,15 @@ def remove_tag(image_name): def get_image_tag(name): return remove_tag(all_images[name]) + def resolve_dockerfile_name(dockerfile_dir_rel): + """Return the dockerfile filename for the given directory, preferring [env].Dockerfile over Dockerfile.""" + dockerfile_dir_abs = os.path.abspath(os.path.join(output_path, dockerfile_dir_rel)) + for env_name in (env or []): + env_dockerfile = os.path.join(dockerfile_dir_abs, f'{env_name}.Dockerfile') + if os.path.exists(env_dockerfile): + return f'{env_name}.Dockerfile' + return 'Dockerfile' + builds = {} def build_artifact( @@ -67,7 +76,7 @@ def build_artifact( 'image': image_name, 'context': context_path, 'docker': { - 'dockerfile': join(dockerfile_path, 'Dockerfile'), + 'dockerfile': join(dockerfile_path, resolve_dockerfile_name(dockerfile_path)), 'buildArgs': build_args, 'ssh': 'default' } diff --git a/tools/deployment-cli-tools/harness-deployment b/tools/deployment-cli-tools/harness-deployment index 472d095a..106e21bb 100644 --- a/tools/deployment-cli-tools/harness-deployment +++ b/tools/deployment-cli-tools/harness-deployment @@ -143,7 +143,7 @@ if __name__ == "__main__": if args.write_env: write_env_file(helm_values, os.path.join(root_paths[-1], DEPLOYMENT_PATH, ".env"), args.image_cache_url) - create_skaffold_configuration(merged_root_paths, helm_values, backend_deploy=COMPOSE_ENGINE if args.docker_compose else HELM_ENGINE) + create_skaffold_configuration(merged_root_paths, helm_values, backend_deploy=COMPOSE_ENGINE if args.docker_compose else HELM_ENGINE, env=envs) if not args.docker_compose: create_vscode_debug_configuration(root_paths, helm_values) diff --git a/tools/deployment-cli-tools/tests/resources/applications/myapp/dev.Dockerfile b/tools/deployment-cli-tools/tests/resources/applications/myapp/dev.Dockerfile new file mode 100644 index 00000000..04c4eee3 --- /dev/null +++ b/tools/deployment-cli-tools/tests/resources/applications/myapp/dev.Dockerfile @@ -0,0 +1,3 @@ +ARG CLOUDHARNESS_FLASK +FROM $CLOUDHARNESS_FLASK +# dev-specific build diff --git a/tools/deployment-cli-tools/tests/test_codefresh.py b/tools/deployment-cli-tools/tests/test_codefresh.py index 4ce13aef..f454d0b2 100644 --- a/tools/deployment-cli-tools/tests/test_codefresh.py +++ b/tools/deployment-cli-tools/tests/test_codefresh.py @@ -118,7 +118,7 @@ def test_create_codefresh_configuration(): step['working_directory'], os.path.join(CLOUDHARNESS_ROOT, APPS_PATH, "samples")) step = steps["myapp"] - assert step['dockerfile'] == "Dockerfile" + assert step['dockerfile'].endswith('dev.Dockerfile'), f"myapp should use dev.Dockerfile but got {step['dockerfile']}" assert "testprojectname/" in step['image_name'], f"myapp image should have the project name coming from the chart in its path, is {step['image_name']}" for build_argument in step['build_arguments']: if build_argument.startswith("CLOUDHARNESS_FLASK="): @@ -534,3 +534,82 @@ def test_app_depends_on_app(): envs=[], base_image_name=values['name'], helm_values=values, save=False) + + +def test_env_dockerfile_codefresh(): + """When a [env].Dockerfile exists it should be used in the codefresh build step.""" + values = create_helm_chart( + [CLOUDHARNESS_ROOT, RESOURCES], + output_path=OUT, + include=['myapp'], + exclude=['events'], + domain="my.local", + namespace='test', + env='dev', + local=False, + tag=1, + registry='reg' + ) + try: + root_paths = preprocess_build_overrides( + root_paths=[CLOUDHARNESS_ROOT, RESOURCES], + helm_values=values, + merge_build_path=BUILD_MERGE_DIR + ) + build_included = [app['harness']['name'] + for app in values['apps'].values() if 'harness' in app] + + cf = create_codefresh_deployment_scripts(root_paths, include=build_included, + envs=['dev'], + base_image_name=values['name'], + helm_values=values, save=False) + # myapp has dev.Dockerfile so it should be used + myapp_step = cf['steps'][STEP_2]['steps']['myapp'] + assert myapp_step['dockerfile'].endswith('dev.Dockerfile'), \ + f"Expected dev.Dockerfile but got {myapp_step['dockerfile']}" + finally: + shutil.rmtree(BUILD_MERGE_DIR, ignore_errors=True) + + +def test_env_dockerfile_codefresh_fallback(): + """When no [env].Dockerfile exists the regular Dockerfile should be used.""" + values = create_helm_chart( + [CLOUDHARNESS_ROOT, RESOURCES], + output_path=OUT, + include=['samples'], + exclude=['events'], + domain="my.local", + namespace='test', + env='dev', + local=False, + tag=1, + registry='reg' + ) + try: + root_paths = preprocess_build_overrides( + root_paths=[CLOUDHARNESS_ROOT, RESOURCES], + helm_values=values, + merge_build_path=BUILD_MERGE_DIR + ) + build_included = [app['harness']['name'] + for app in values['apps'].values() if 'harness' in app] + + # samples has no dev.Dockerfile, so it should fall back to Dockerfile + cf = create_codefresh_deployment_scripts(root_paths, include=build_included, + envs=['dev'], + base_image_name=values['name'], + helm_values=values, save=False) + all_build_steps = { + step_name: step + for build_step_name in [STEP_0, STEP_1, STEP_2, STEP_3] + if build_step_name in cf['steps'] + for step_name, step in cf['steps'][build_step_name]['steps'].items() + } + assert 'samples' in all_build_steps, "samples should be in the build steps" + samples_step = all_build_steps['samples'] + assert samples_step['dockerfile'] == 'Dockerfile', \ + f"Expected Dockerfile but got {samples_step['dockerfile']}" + assert not samples_step['dockerfile'].endswith('dev.Dockerfile'), \ + "samples should not use dev.Dockerfile as it does not have one" + finally: + shutil.rmtree(BUILD_MERGE_DIR, ignore_errors=True) diff --git a/tools/deployment-cli-tools/tests/test_skaffold.py b/tools/deployment-cli-tools/tests/test_skaffold.py index 5898e37e..11aa33ec 100644 --- a/tools/deployment-cli-tools/tests/test_skaffold.py +++ b/tools/deployment-cli-tools/tests/test_skaffold.py @@ -217,6 +217,92 @@ def test_create_skaffold_configuration_nobuild(tmp_path): assert 'myapp' not in release['overrides']['apps'] +def test_env_dockerfile(tmp_path): + """When a [env].Dockerfile exists it should be used instead of Dockerfile.""" + values = create_helm_chart( + [CLOUDHARNESS_ROOT, RESOURCES], + output_path=tmp_path, + include=['samples', 'myapp'], + exclude=['events'], + domain="my.local", + namespace='test', + env='dev', + local=False, + tag=1, + registry='reg' + ) + BUILD_DIR = "/tmp/build" + root_paths = preprocess_build_overrides( + root_paths=[CLOUDHARNESS_ROOT, RESOURCES], + helm_values=values, + merge_build_path=BUILD_DIR + ) + + sk = create_skaffold_configuration( + root_paths=root_paths, + helm_values=values, + output_path=tmp_path, + env=['dev'] + ) + + myapp_artifact = next( + a for a in sk['build']['artifacts'] if a['image'] == f'reg/testprojectname/myapp') + # myapp has a dev.Dockerfile so it should be used + assert myapp_artifact['docker']['dockerfile'].endswith('dev.Dockerfile'), \ + f"Expected dev.Dockerfile but got {myapp_artifact['docker']['dockerfile']}" + + # samples has no dev.Dockerfile, so it should fall back to Dockerfile + expected_samples_image = values[KEY_APPS]['samples'][KEY_HARNESS][KEY_DEPLOYMENT]['image'].split(':')[0] + samples_artifact = next( + a for a in sk['build']['artifacts'] if a['image'] == expected_samples_image) + assert samples_artifact['docker']['dockerfile'].endswith('Dockerfile'), \ + f"Expected Dockerfile but got {samples_artifact['docker']['dockerfile']}" + assert not samples_artifact['docker']['dockerfile'].endswith('dev.Dockerfile'), \ + "samples should not use dev.Dockerfile" + + shutil.rmtree(tmp_path) + shutil.rmtree(BUILD_DIR) + + +def test_env_dockerfile_fallback(tmp_path): + """Without env, or when no env.Dockerfile exists, the regular Dockerfile should be used.""" + values = create_helm_chart( + [CLOUDHARNESS_ROOT, RESOURCES], + output_path=tmp_path, + include=['myapp'], + exclude=['events'], + domain="my.local", + namespace='test', + env='', + local=False, + tag=1, + registry='reg' + ) + BUILD_DIR = "/tmp/build2" + root_paths = preprocess_build_overrides( + root_paths=[CLOUDHARNESS_ROOT, RESOURCES], + helm_values=values, + merge_build_path=BUILD_DIR + ) + + sk = create_skaffold_configuration( + root_paths=root_paths, + helm_values=values, + output_path=tmp_path, + env=None + ) + + myapp_artifact = next( + a for a in sk['build']['artifacts'] if a['image'] == f'reg/testprojectname/myapp') + assert myapp_artifact['docker']['dockerfile'].endswith('Dockerfile'), \ + f"Expected Dockerfile but got {myapp_artifact['docker']['dockerfile']}" + assert not myapp_artifact['docker']['dockerfile'].endswith('dev.Dockerfile'), \ + "Should not use dev.Dockerfile when no env is specified" + + shutil.rmtree(tmp_path) + shutil.rmtree(BUILD_DIR) + + def test_app_depends_on_app(tmp_path): out_folder = tmp_path / 'test_app_depends_on_app' From b120de41b0f9780fcda2047f2ad27b24cdcd6999 Mon Sep 17 00:00:00 2001 From: Filippo Ledda Date: Wed, 11 Mar 2026 19:29:47 +0100 Subject: [PATCH 32/33] CH-251 start network policy implementation --- .../helm/templates/auto-network-policies.yaml | 67 ++++++ .../helm/templates/httproute.yaml | 192 ++++++++++++++++++ deployment-configuration/value-template.yaml | 7 +- 3 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 deployment-configuration/helm/templates/auto-network-policies.yaml create mode 100644 deployment-configuration/helm/templates/httproute.yaml diff --git a/deployment-configuration/helm/templates/auto-network-policies.yaml b/deployment-configuration/helm/templates/auto-network-policies.yaml new file mode 100644 index 00000000..fb78917f --- /dev/null +++ b/deployment-configuration/helm/templates/auto-network-policies.yaml @@ -0,0 +1,67 @@ +{{- define "deploy_utils.network_policy" }} +{{- $hasNetwork := hasKey .app.harness "network" }} +{{- $allowIngress := and $hasNetwork .app.harness.network.allow_ingress }} +{{- $allowEgress := and $hasNetwork .app.harness.network.allow_egress }} +{{- if $hasNetwork }} +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ printf "%s-network-policy" .app.harness.deployment.name }} + namespace: {{ .root.Values.namespace }} + labels: + app: {{ .app.harness.deployment.name | quote }} +{{- include "deploy_utils.labels" .root | indent 4 }} +spec: + podSelector: + matchLabels: + app: {{ .app.harness.deployment.name | quote }} + policyTypes: + - Ingress + - Egress + ingress: + # Always allow traffic from pods in the same namespace + - from: + - podSelector: {} + {{- if $allowIngress }} + {{- if or .app.harness.subdomain .app.harness.domain .app.harness.aliases }} + # Allow traffic from the ingress controller (any namespace) when ingress is configured + - from: + - namespaceSelector: {} + {{- end }} + {{- end }} + egress: + # Always allow traffic to pods in the same namespace + - to: + - podSelector: {} + # Always allow DNS queries + - ports: + - port: 53 + protocol: UDP + - port: 53 + protocol: TCP + {{- if $allowEgress }} + # Allow traffic to the internet (non-private IPs) + - to: + - ipBlock: + cidr: 0.0.0.0/0 + except: + - 10.0.0.0/8 + - 172.16.0.0/12 + - 192.168.0.0/16 + {{- end }} +{{- end }} +{{- end }} + +{{- range $app := .Values.apps }} + {{- if and (hasKey $app "port") $app.harness.deployment.auto | default false }} + {{- include "deploy_utils.network_policy" (dict "root" $ "app" $app) }} + {{- end }} + {{- range $subapp := $app }} + {{- if contains "map" (typeOf $subapp) }} + {{- if hasKey $subapp "harness" }} + {{- include "deploy_utils.network_policy" (dict "root" $ "app" $subapp) }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/deployment-configuration/helm/templates/httproute.yaml b/deployment-configuration/helm/templates/httproute.yaml new file mode 100644 index 00000000..4b619078 --- /dev/null +++ b/deployment-configuration/helm/templates/httproute.yaml @@ -0,0 +1,192 @@ +{{/* + Renders the HTTPRoute rules for a standard app. + Equivalent of deploy_utils.ingress.http but for Gateway API. + White-listed URI role mapping paths go to the direct backend; the catch-all + rule goes to the gatekeeper when secured_gatekeepers is enabled. +*/}} +{{- define "deploy_utils.httproute.rules" }} + {{- $app := .app }} + {{- $root := .root }} + {{- $subdomain := .subdomain }} + {{- $secured_gatekeepers := and $root.Values.secured_gatekeepers }} + {{- if and $app.harness.secured $secured_gatekeepers $app.harness.uri_role_mapping (eq $app.harness.gateway.pathType "Prefix") }} + {{- range $mapping := $app.harness.uri_role_mapping }} + {{- if and (hasKey $mapping "white-listed") (index $mapping "white-listed") }} + {{- $uri := $mapping.uri }} + {{- if eq $uri "/" }} + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: {{ $app.harness.service.name }} + port: {{ $app.harness.service.port | default 80 }} + {{- else if hasSuffix "/*" $uri }} + {{- $cleanPath := trimSuffix "/*" $uri }} + {{- $pathWithoutSlash := trimPrefix "/" $cleanPath }} + - matches: + - path: + type: PathPrefix + value: /{{ $pathWithoutSlash }}/ + backendRefs: + - name: {{ $app.harness.service.name }} + port: {{ $app.harness.service.port | default 80 }} + {{- else if (not (contains "*" $uri)) }} + {{- $pathWithoutSlash := trimPrefix "/" $uri }} + - matches: + - path: + type: PathPrefix + value: {{ printf "%s/%s" $app.harness.gateway.path $pathWithoutSlash }} + backendRefs: + - name: {{ $app.harness.service.name }} + port: {{ $app.harness.service.port | default 80 }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + - matches: + - path: + type: PathPrefix + value: {{ $app.harness.gateway.path | default $root.Values.ingress.path | default "/" }} + backendRefs: + - name: {{ if (and $app.harness.secured $secured_gatekeepers) }}{{ printf "%s-gk" $subdomain }}{{ else }}{{ $app.harness.service.name }}{{ end }} + port: {{ if (and $app.harness.secured $secured_gatekeepers) }}8080{{ else }}{{ $app.harness.service.port | default 80 }}{{ end }} +{{- end }} + +{{/* + Renders HTTPRoute rules for proxy (use_services) routes. + Equivalent of deploy_utils.ingress.service but for Gateway API. + Uses URLRewrite filter to strip /proxy// prefix before forwarding. +*/}} +{{- define "deploy_utils.httproute.proxy.rules" }} + {{- $root := .root }} + {{- $secured_gatekeepers := and $root.Values.secured_gatekeepers }} + {{- range $service := .use_services }} + {{- $svcApp := get $root.Values.apps $service.name }} + - matches: + - path: + type: PathPrefix + value: /proxy/{{ $svcApp.harness.service.name }}/ + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplacePrefixMatch + replacePrefixMatch: / + backendRefs: + - name: {{ $svcApp.harness.service.name }}{{- if (and $svcApp.harness.secured $secured_gatekeepers) }}-gk{{- end }} + port: {{ if (and $svcApp.harness.secured $secured_gatekeepers) }}8080{{ else }}{{ $svcApp.harness.service.port | default 80 }}{{ end }} + {{- end }} +{{- end }} + +{{- if and .Values.ingress.enabled .Values.ingress.useGatewayAPI }} + {{- $domain := .Values.domain }} + {{- $tls := not (not .Values.tls) }} + {{- $mainapp := .Values.mainapp }} +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: {{ .Values.ingress.name | quote }} + {{- if and (not .Values.local) $tls }} + annotations: + cert-manager.io/issuer: {{ printf "%s-%s" "letsencrypt" .Values.namespace }} + {{- end }} +spec: + gatewayClassName: {{ .Values.ingress.ingressClass }} + listeners: + - name: http + port: 80 + protocol: HTTP + allowedRoutes: + namespaces: + from: Same + {{- if $tls }} + - name: https + port: 443 + protocol: HTTPS + hostname: {{ printf "*.%s" $domain | quote }} + tls: + mode: Terminate + certificateRefs: + - name: tls-secret + allowedRoutes: + namespaces: + from: Same + {{- if $mainapp }} + - name: https-root + port: 443 + protocol: HTTPS + hostname: {{ $domain | quote }} + tls: + mode: Terminate + certificateRefs: + - name: tls-secret + allowedRoutes: + namespaces: + from: Same + {{- end }} + {{- end }} + {{- range $app := .Values.apps }} + {{- if or $app.harness.subdomain $app.harness.domain $app.harness.aliases (and $mainapp (and $app.harness.name (eq $app.harness.name $mainapp))) }} + {{- $appIngressName := default $app.harness.name $app.harness.service.name }} +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: {{ $appIngressName | quote }} +spec: + parentRefs: + - name: {{ $.Values.ingress.name }} + namespace: {{ $.Values.namespace }} + hostnames: + {{- if (and $mainapp (and $app.harness.name (eq $app.harness.name $mainapp))) }} + {{- if ne $app.harness.subdomain "www" }} + - {{ $domain | quote }} + {{- end }} + {{- end }} + {{- if $app.harness.domain }} + - {{ $app.harness.domain | quote }} + {{- end }} + {{- if $app.harness.aliases }} + {{- range $alias := $app.harness.aliases }} + - {{ printf "%s.%s" $alias $domain | quote }} + {{- end }} + {{- end }} + {{- if $app.harness.subdomain }} + - {{ printf "%s.%s" $app.harness.subdomain $domain | quote }} + {{- range $subapp := $app }} + {{- if contains "map" (typeOf $subapp) }} + {{- if and $subapp (hasKey $subapp "harness.subdomain") }} + - {{ printf "%s.%s.%s" $subapp.harness.subdomain $app.harness.subdomain $domain | quote }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + rules: + {{ include "deploy_utils.httproute.rules" (dict "root" $ "app" $app "subdomain" $app.harness.subdomain) }} + {{- if $app.harness.use_services }} +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: {{ printf "%s-proxy" $appIngressName | quote }} +spec: + parentRefs: + - name: {{ $.Values.ingress.name }} + namespace: {{ $.Values.namespace }} + hostnames: + {{- if (and $mainapp (and $app.harness.name (eq $app.harness.name $mainapp))) }} + {{- if ne $app.harness.subdomain "www" }} + - {{ $domain | quote }} + {{- end }} + {{- end }} + {{- if $app.harness.subdomain }} + - {{ printf "%s.%s" $app.harness.subdomain $domain | quote }} + {{- end }} + rules: + {{ include "deploy_utils.httproute.proxy.rules" (dict "root" $ "use_services" $app.harness.use_services) }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/deployment-configuration/value-template.yaml b/deployment-configuration/value-template.yaml index cb1f964e..124b957d 100644 --- a/deployment-configuration/value-template.yaml +++ b/deployment-configuration/value-template.yaml @@ -154,4 +154,9 @@ harness: ## -- The path mapped to the current application in the gateway. Default is "/". For regex paths, use the path specified in .Values.ingress.path and set pathType to ImplementationSpecific path: "/" ## -- The pathType for the Ingress path. Default is Prefix. For regex paths, set to ImplementationSpecific - pathType: Prefix \ No newline at end of file + pathType: Prefix + network: + # -- If true, creates a network policy allowing ingress traffic to the application service from other services in the same namespace and from the internet if ingress is enabled. + allow_ingress: true + # -- If true, creates a network policy allowing egress traffic from the application pods to other services in the same namespace and to the internet. + allow_egress: true \ No newline at end of file From 2046fc50304635ab37959442490f4bd0b0aa2446 Mon Sep 17 00:00:00 2001 From: Filippo Ledda Date: Wed, 11 Mar 2026 19:30:54 +0100 Subject: [PATCH 33/33] chore: test value fix --- deployment/codefresh-test.yaml | 23 +++++++++++++++---- .../applications/myapp/deploy/values.yaml | 2 +- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/deployment/codefresh-test.yaml b/deployment/codefresh-test.yaml index 0012e877..b4ad6b8d 100644 --- a/deployment/codefresh-test.yaml +++ b/deployment/codefresh-test.yaml @@ -13,13 +13,26 @@ steps: repo: '${{CF_REPO_OWNER}}/${{CF_REPO_NAME}}' revision: '${{CF_BRANCH}}' git: github + post_main_clone: + title: Post main clone + type: parallel + stage: prepare + steps: + clone_cloud_harness: + title: Cloning cloud-harness repository... + type: git-clone + stage: prepare + repo: https://github.com/MetaCell/cloud-harness.git + revision: '${{CLOUDHARNESS_BRANCH}}' + working_directory: . + git: github prepare_deployment: title: Prepare helm chart image: python:3.12 stage: prepare working_directory: . commands: - - bash ./install.sh + - bash cloud-harness/install.sh - export HELM_NAME_ARG="$( [ -n "${{CHART_NAME}}" ] && printf -- "--name %s" "${{CHART_NAME}}" )" - export HELM_CHART_VERSION_ARG="$( [ -n "${{CHART_VERSION}}" ] && printf -- "--chart-version @@ -509,13 +522,13 @@ steps: commands: - kubectl config use-context ${{CLUSTER_NAME}} - kubectl config set-context --current --namespace=test-${{NAMESPACE_BASENAME}} - - kubectl rollout status deployment/volumemanager - - kubectl rollout status deployment/argo-gk - kubectl rollout status deployment/samples - kubectl rollout status deployment/samples-gk - - kubectl rollout status deployment/accounts - - kubectl rollout status deployment/common - kubectl rollout status deployment/workflows + - kubectl rollout status deployment/common + - kubectl rollout status deployment/accounts + - kubectl rollout status deployment/volumemanager + - kubectl rollout status deployment/argo-gk - sleep 60 tests_api: stage: qa diff --git a/tools/deployment-cli-tools/tests/resources/applications/myapp/deploy/values.yaml b/tools/deployment-cli-tools/tests/resources/applications/myapp/deploy/values.yaml index 8c662f66..44bd78e9 100644 --- a/tools/deployment-cli-tools/tests/resources/applications/myapp/deploy/values.yaml +++ b/tools/deployment-cli-tools/tests/resources/applications/myapp/deploy/values.yaml @@ -4,7 +4,7 @@ harness: database: auto: true type: mongo - external_connect_string: "" + connect_string: "" dependencies: soft: - legacy