From 413ffdc27b2b182e2445c1632568679a6d442089 Mon Sep 17 00:00:00 2001 From: Antonio Cascais Date: Sat, 11 Apr 2026 13:14:46 +0200 Subject: [PATCH 1/2] feat: add prod apps for waves 6-7 (keycloak, auth) Keycloak (wave 6): prod hostnames, log level INFO, theme + plugins. Auth (wave 7): prod hostnames, env=prod. Docs: added missing github-token and freeipa-password to vault-secrets.md. --- clusters/prod/apps/auth/Chart.yaml | 7 + clusters/prod/apps/auth/application.yaml | 28 + .../apps/auth/templates/external-secret.yaml | 29 + clusters/prod/apps/auth/values.yaml | 103 +++ clusters/prod/apps/keycloak/Chart.yaml | 7 + clusters/prod/apps/keycloak/application.yaml | 28 + .../keycloak/templates/external-secret.yaml | 53 ++ .../keycloak/templates/keycloak-antd-pvc.yaml | 12 + .../keycloak/templates/theme-configmap.yaml | 33 + .../prod/apps/keycloak/theme/login/login.ftl | 74 ++ .../theme/login/resources/css/login.css | 687 ++++++++++++++++++ .../theme/login/resources/img/HDC-logo.png | Bin 0 -> 19646 bytes .../theme/login/resources/img/error.png | Bin 0 -> 324 bytes .../theme/login/resources/img/favicon.ico | Bin 0 -> 627 bytes .../img/feedback-error-arrow-down.png | Bin 0 -> 513 bytes .../resources/img/feedback-error-sign.png | Bin 0 -> 343 bytes .../img/feedback-success-arrow-down.png | Bin 0 -> 678 bytes .../resources/img/feedback-success-sign.png | Bin 0 -> 410 bytes .../img/feedback-warning-arrow-down.png | Bin 0 -> 513 bytes .../resources/img/feedback-warning-sign.png | Bin 0 -> 646 bytes .../theme/login/resources/img/keycloak-bg.png | Bin 0 -> 81862 bytes .../resources/img/keycloak-logo-text.png | Bin 0 -> 19994 bytes .../login/resources/img/keycloak-logo.png | Bin 0 -> 5281 bytes .../theme/login/resources/img/lock.png | Bin 0 -> 445 bytes .../theme/login/resources/img/user.png | Bin 0 -> 683 bytes .../theme/login/resources/js/jquery.min.js | 2 + .../theme/login/resources/js/login.js | 5 + .../apps/keycloak/theme/login/template.ftl | 147 ++++ .../keycloak/theme/login/theme.properties | 95 +++ clusters/prod/apps/keycloak/values.yaml | 135 ++++ docs/vault-secrets.md | 6 +- 31 files changed, 1449 insertions(+), 2 deletions(-) create mode 100644 clusters/prod/apps/auth/Chart.yaml create mode 100644 clusters/prod/apps/auth/application.yaml create mode 100644 clusters/prod/apps/auth/templates/external-secret.yaml create mode 100644 clusters/prod/apps/auth/values.yaml create mode 100644 clusters/prod/apps/keycloak/Chart.yaml create mode 100644 clusters/prod/apps/keycloak/application.yaml create mode 100644 clusters/prod/apps/keycloak/templates/external-secret.yaml create mode 100644 clusters/prod/apps/keycloak/templates/keycloak-antd-pvc.yaml create mode 100644 clusters/prod/apps/keycloak/templates/theme-configmap.yaml create mode 100644 clusters/prod/apps/keycloak/theme/login/login.ftl create mode 100644 clusters/prod/apps/keycloak/theme/login/resources/css/login.css create mode 100644 clusters/prod/apps/keycloak/theme/login/resources/img/HDC-logo.png create mode 100644 clusters/prod/apps/keycloak/theme/login/resources/img/error.png create mode 100644 clusters/prod/apps/keycloak/theme/login/resources/img/favicon.ico create mode 100644 clusters/prod/apps/keycloak/theme/login/resources/img/feedback-error-arrow-down.png create mode 100644 clusters/prod/apps/keycloak/theme/login/resources/img/feedback-error-sign.png create mode 100644 clusters/prod/apps/keycloak/theme/login/resources/img/feedback-success-arrow-down.png create mode 100644 clusters/prod/apps/keycloak/theme/login/resources/img/feedback-success-sign.png create mode 100644 clusters/prod/apps/keycloak/theme/login/resources/img/feedback-warning-arrow-down.png create mode 100644 clusters/prod/apps/keycloak/theme/login/resources/img/feedback-warning-sign.png create mode 100644 clusters/prod/apps/keycloak/theme/login/resources/img/keycloak-bg.png create mode 100644 clusters/prod/apps/keycloak/theme/login/resources/img/keycloak-logo-text.png create mode 100644 clusters/prod/apps/keycloak/theme/login/resources/img/keycloak-logo.png create mode 100644 clusters/prod/apps/keycloak/theme/login/resources/img/lock.png create mode 100644 clusters/prod/apps/keycloak/theme/login/resources/img/user.png create mode 100644 clusters/prod/apps/keycloak/theme/login/resources/js/jquery.min.js create mode 100644 clusters/prod/apps/keycloak/theme/login/resources/js/login.js create mode 100644 clusters/prod/apps/keycloak/theme/login/template.ftl create mode 100644 clusters/prod/apps/keycloak/theme/login/theme.properties create mode 100644 clusters/prod/apps/keycloak/values.yaml diff --git a/clusters/prod/apps/auth/Chart.yaml b/clusters/prod/apps/auth/Chart.yaml new file mode 100644 index 0000000..16c3780 --- /dev/null +++ b/clusters/prod/apps/auth/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: auth +version: 0.1.0 +dependencies: + - name: auth-service + version: "1.0.9" + repository: https://pilotdataplatform.github.io/helm-charts/ diff --git a/clusters/prod/apps/auth/application.yaml b/clusters/prod/apps/auth/application.yaml new file mode 100644 index 0000000..b3c8e67 --- /dev/null +++ b/clusters/prod/apps/auth/application.yaml @@ -0,0 +1,28 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: auth + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "7" +spec: + project: default + source: + repoURL: https://github.com/PilotDataPlatform/pilot-hdc-platform-gitops.git + targetRevision: main + path: clusters/prod/apps/auth + helm: + valueFiles: + - ../../registry.yaml + - ../../versions.yaml + - values.yaml + destination: + server: https://kubernetes.default.svc + namespace: utility + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/clusters/prod/apps/auth/templates/external-secret.yaml b/clusters/prod/apps/auth/templates/external-secret.yaml new file mode 100644 index 0000000..0ff8471 --- /dev/null +++ b/clusters/prod/apps/auth/templates/external-secret.yaml @@ -0,0 +1,29 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: auth-credentials + namespace: utility +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: vault + target: + name: auth-credentials + data: + - secretKey: auth-user-password + remoteRef: + key: secret/data/postgresql + property: auth-user-password + - secretKey: redis-password + remoteRef: + key: secret/data/redis + property: password + - secretKey: keycloak-client-secret + remoteRef: + key: secret/data/auth + property: keycloak-client-secret + - secretKey: freeipa-password + remoteRef: + key: secret/data/auth + property: freeipa-password diff --git a/clusters/prod/apps/auth/values.yaml b/clusters/prod/apps/auth/values.yaml new file mode 100644 index 0000000..4b6a0c3 --- /dev/null +++ b/clusters/prod/apps/auth/values.yaml @@ -0,0 +1,103 @@ +auth-service: + image: + repository: n47w5524.c1.de1.container-registry.ovh.net/hdc-services-image/auth + pullPolicy: IfNotPresent + + fullnameOverride: auth + replicaCount: 3 + + container: + port: 5061 + + service: + type: ClusterIP + port: 5061 + targetPort: 5061 + + imagePullSecrets: + - name: docker-registry-secret + + appConfig: + env: prod + WORKERS: 2 + RDS_HOST: postgres.utility + RDS_USER: auth_user + RDS_DBNAME: auth + RDS_SCHEMA_PREFIX: pilot + LDAP_PREFIX: hdc + LDAP_COMMON_NAME_PREFIX: hdc + AD_USER_GROUP: hdc-users + IDENTITY_BACKEND: keycloak + KEYCLOAK_SERVER_URL: "https://iam.hdc.ebrains.eu/" + KEYCLOAK_CLIENT_ID: kong + KEYCLOAK_REALM: hdc + KEYCLOAK_ID: hdc + REDIS_HOST: redis-master.redis + REDIS_PORT: "6379" + FREEIPA_URL: "ldap.hdc.ebrains.eu" + ENABLE_ACTIVE_DIRECTORY: "false" + DOMAIN_NAME: "https://portal.hdc.ebrains.eu" + START_PATH: hdc + GUIDE_PATH: "" + EMAIL_SUPPORT: support@hdc.ebrains.eu + EMAIL_ADMIN: admin@hdc.ebrains.eu + EMAIL_HELPDESK: helpdesk@hdc.ebrains.eu + PLATFORM_NAME: HDC + PROJECT_NAME: HDC + OPEN_TELEMETRY_ENABLED: "false" + config_center_enabled: "false" + + resources: + limits: + cpu: 500m + memory: 500Mi + requests: + cpu: 25m + memory: 10Mi + + extraEnvYaml: + - name: RDS_PWD + valueFrom: + secretKeyRef: + name: auth-credentials + key: auth-user-password + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: auth-credentials + key: redis-password + - name: KEYCLOAK_SECRET + valueFrom: + secretKeyRef: + name: auth-credentials + key: keycloak-client-secret + - name: FREEIPA_PASSWORD + valueFrom: + secretKeyRef: + name: auth-credentials + key: freeipa-password + + extraEnv: + REDIS_DB: "0" + RDS_PRE_PING: "true" + + readinessProbe: + tcpSocket: + port: 5061 + initialDelaySeconds: 10 + periodSeconds: 10 + failureThreshold: 3 + + livenessProbe: + httpGet: + path: /v1/health + port: 5061 + periodSeconds: 30 + timeoutSeconds: 5 + failureThreshold: 3 + + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 diff --git a/clusters/prod/apps/keycloak/Chart.yaml b/clusters/prod/apps/keycloak/Chart.yaml new file mode 100644 index 0000000..d8cb845 --- /dev/null +++ b/clusters/prod/apps/keycloak/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: keycloak +version: 0.1.0 +dependencies: + - name: keycloak + version: "13.2.0" + repository: https://pilotdataplatform.github.io/helm-charts/ diff --git a/clusters/prod/apps/keycloak/application.yaml b/clusters/prod/apps/keycloak/application.yaml new file mode 100644 index 0000000..e270e5b --- /dev/null +++ b/clusters/prod/apps/keycloak/application.yaml @@ -0,0 +1,28 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: keycloak + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "6" +spec: + project: default + source: + repoURL: https://github.com/PilotDataPlatform/pilot-hdc-platform-gitops.git + targetRevision: main + path: clusters/prod/apps/keycloak + helm: + valueFiles: + - ../../registry.yaml + - ../../versions.yaml + - values.yaml + destination: + server: https://kubernetes.default.svc + namespace: keycloak + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/clusters/prod/apps/keycloak/templates/external-secret.yaml b/clusters/prod/apps/keycloak/templates/external-secret.yaml new file mode 100644 index 0000000..497b48a --- /dev/null +++ b/clusters/prod/apps/keycloak/templates/external-secret.yaml @@ -0,0 +1,53 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: keycloak-credentials + namespace: keycloak +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: vault + target: + name: keycloak-credentials + data: + - secretKey: admin-password + remoteRef: + key: secret/data/keycloak + property: admin-password +--- +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: keycloak-db-credentials + namespace: keycloak +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: vault + target: + name: keycloak-db-credentials + data: + - secretKey: password + remoteRef: + key: secret/data/keycloak + property: keycloak-user-password +--- +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: github-access-token + namespace: keycloak +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: vault + target: + name: github-access-token + data: + - secretKey: GITHUB_TOKEN + remoteRef: + key: secret/data/keycloak + property: github-token diff --git a/clusters/prod/apps/keycloak/templates/keycloak-antd-pvc.yaml b/clusters/prod/apps/keycloak/templates/keycloak-antd-pvc.yaml new file mode 100644 index 0000000..6ef116a --- /dev/null +++ b/clusters/prod/apps/keycloak/templates/keycloak-antd-pvc.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: keycloak-antd + namespace: keycloak +spec: + accessModes: + - ReadWriteOnce + storageClassName: csi-cinder-high-speed + resources: + requests: + storage: 1Gi diff --git a/clusters/prod/apps/keycloak/templates/theme-configmap.yaml b/clusters/prod/apps/keycloak/templates/theme-configmap.yaml new file mode 100644 index 0000000..39cd43f --- /dev/null +++ b/clusters/prod/apps/keycloak/templates/theme-configmap.yaml @@ -0,0 +1,33 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: keycloak-antd-theme + namespace: keycloak +data: + login.ftl: | +{{ .Files.Get "theme/login/login.ftl" | indent 4 }} + template.ftl: | +{{ .Files.Get "theme/login/template.ftl" | indent 4 }} + theme.properties: | +{{ .Files.Get "theme/login/theme.properties" | indent 4 }} + login.css: | +{{ .Files.Get "theme/login/resources/css/login.css" | indent 4 }} + jquery.min.js: | +{{ .Files.Get "theme/login/resources/js/jquery.min.js" | indent 4 }} + login.js: | +{{ .Files.Get "theme/login/resources/js/login.js" | indent 4 }} +binaryData: + error.png: {{ .Files.Get "theme/login/resources/img/error.png" | b64enc | quote }} + favicon.ico: {{ .Files.Get "theme/login/resources/img/favicon.ico" | b64enc | quote }} + feedback-error-arrow-down.png: {{ .Files.Get "theme/login/resources/img/feedback-error-arrow-down.png" | b64enc | quote }} + feedback-error-sign.png: {{ .Files.Get "theme/login/resources/img/feedback-error-sign.png" | b64enc | quote }} + feedback-success-arrow-down.png: {{ .Files.Get "theme/login/resources/img/feedback-success-arrow-down.png" | b64enc | quote }} + feedback-success-sign.png: {{ .Files.Get "theme/login/resources/img/feedback-success-sign.png" | b64enc | quote }} + feedback-warning-arrow-down.png: {{ .Files.Get "theme/login/resources/img/feedback-warning-arrow-down.png" | b64enc | quote }} + feedback-warning-sign.png: {{ .Files.Get "theme/login/resources/img/feedback-warning-sign.png" | b64enc | quote }} + HDC-logo.png: {{ .Files.Get "theme/login/resources/img/HDC-logo.png" | b64enc | quote }} + keycloak-bg.png: {{ .Files.Get "theme/login/resources/img/keycloak-bg.png" | b64enc | quote }} + keycloak-logo.png: {{ .Files.Get "theme/login/resources/img/keycloak-logo.png" | b64enc | quote }} + keycloak-logo-text.png: {{ .Files.Get "theme/login/resources/img/keycloak-logo-text.png" | b64enc | quote }} + lock.png: {{ .Files.Get "theme/login/resources/img/lock.png" | b64enc | quote }} + user.png: {{ .Files.Get "theme/login/resources/img/user.png" | b64enc | quote }} diff --git a/clusters/prod/apps/keycloak/theme/login/login.ftl b/clusters/prod/apps/keycloak/theme/login/login.ftl new file mode 100644 index 0000000..5e5e8b3 --- /dev/null +++ b/clusters/prod/apps/keycloak/theme/login/login.ftl @@ -0,0 +1,74 @@ +<#import "template.ftl" as layout> +<@layout.registrationLayout displayInfo=social.displayInfo displayWide=(realm.password && social.providers??); section> + <#if section = "header"> + <#-- ${msg("doLogIn")} --> + + <#elseif section = "form"> + +
class="${properties.kcContentWrapperClass!}"> +
class="${properties.kcFormSocialAccountContentClass!} ${properties.kcFormSocialAccountClass!}"> + <#if realm.password> +
+
+ + <#if usernameEditDisabled??> + + <#else> + + +
+
+ + +
+
+
+ <#if realm.rememberMe && !usernameEditDisabled??> +
+ +
+ +
+
+ <#if realm.resetPasswordAllowed> + + +
+ +
+ +
+ value="${auth.selectedCredential}"/> + +
+
+
+ or login with username and password +
+ +
+ <#if realm.password && social.providers??> +
+ +
+ +
+ <#elseif section = "info" > + <#if realm.password && realm.registrationAllowed && !registrationDisabled??> +
+ ${msg("noAccount")} ${msg("doRegister")} +
+ + + + diff --git a/clusters/prod/apps/keycloak/theme/login/resources/css/login.css b/clusters/prod/apps/keycloak/theme/login/resources/css/login.css new file mode 100644 index 0000000..c92a780 --- /dev/null +++ b/clusters/prod/apps/keycloak/theme/login/resources/css/login.css @@ -0,0 +1,687 @@ +.login-pf body { + background: none; + background-color: #f0f2f5; + background-size: cover; + height: 100%; +} + +.label-icon{ + width:15px; margin-top:-5px; +} + +.label-text{ + margin-left: 8px; +} + +.forget-password{ + margin-bottom: 8px; +} + +.login-pf-page-header{ + display: none; +} + +.login-pf-page{ + padding-top: 117px; +} + +/* .card-pf{ + position: absolute ; + margin: auto ; +} */ + +.form-div{ + padding-top: 30px; +} + +.btn { + line-height: 1.5715; + position: relative; + display: inline-block; + font-weight: 400; + white-space: nowrap; + text-align: center; + background-image: none; + border: 1px solid transparent; + -webkit-box-shadow: 0 2px 0 rgba(0, 0, 0, 0.015); + box-shadow: 0 2px 0 rgba(0, 0, 0, 0.015); + cursor: pointer; + -webkit-transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); + transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -ms-touch-action: manipulation; + touch-action: manipulation; + height: 32px; + padding: 4px 15px; + font-size: 14px; + border-radius: 2px; + color: rgba(0, 0, 0, 0.65); + background-color: #fff; + border-color: #d9d9d9; + } + + .btn-primary { + color: #fff; + background-color: #5bab58; + border-color: #5bab58; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.12); + -webkit-box-shadow: 0 2px 0 rgba(0, 0, 0, 0.045); + box-shadow: 0 2px 0 rgba(0, 0, 0, 0.045); + } + + .alert { + -webkit-box-sizing: border-box; + box-sizing: border-box; + margin: 0; + padding: 0; + color: rgba(0, 0, 0, 0.65); + font-size: 14px; + font-variant: tabular-nums; + line-height: 1.5715; + list-style: none; + -webkit-font-feature-settings: 'tnum'; + font-feature-settings: 'tnum'; + position: relative; + padding: 8px 15px 8px 15px; + word-wrap: break-word; + border-radius: 2px; + } +.alert-error { + background-color: #fff2f0; + border: 1px solid #ffccc7; +} + +.alert-success{ + background-color: #f6ffed; + border: 1px solid #b7eb8f; + color: #52c41a; +} + +.alert-warning{ + background-color: #fffbe6; + border: 1px solid #ffe58f; +} + +.alert-info{ + background-color: #e6f7ff; + border: 1px solid #91d5ff; +} + +#kc-locale ul { + display: none; + position: absolute; + background-color: #fff; + list-style: none; + right: 0; + top: 20px; + min-width: 100px; + padding: 2px 0; + border: solid 1px #bbb; +} + +#kc-locale:hover ul { + display: block; + margin: 0; +} + +#kc-locale ul li a { + display: block; + padding: 5px 14px; + color: #000 !important; + text-decoration: none; + line-height: 20px; +} + +#kc-locale ul li a:hover { + color: #4d5258; + background-color: #d4edfa; +} + +#kc-locale-dropdown a { + color: #4d5258; + background: 0 0; + padding: 0 15px 0 0; + font-weight: 300; +} + +#kc-locale-dropdown a:hover { + text-decoration: none; +} + +a#kc-current-locale-link { + display: block; + padding: 0 5px; +} + +/* a#kc-current-locale-link:hover { + background-color: rgba(0,0,0,0.2); +} */ + +a#kc-current-locale-link::after { + content: "\2c5"; + margin-left: 4px; +} + +.login-pf .container { + padding-top: 40px; +} + +.login-pf a:hover { + color: #5bab58; +} + +#kc-logo { + width: 100%; +} + +#kc-logo-wrapper { + background-image: url(../img/keycloak-logo-2.png); + background-repeat: no-repeat; + height: 63px; + width: 300px; + margin: 62px auto 0; +} + +div.kc-logo-text { + background-image: url(../img/keycloak-logo-text.png); + background-repeat: no-repeat; + height: 63px; + width: 300px; + margin: 0 auto; +} + +div.kc-logo-text span { + display: none; +} + +#kc-header { + color: #ededed; + overflow: visible; + white-space: nowrap; +} + +#kc-header-wrapper { + display: none; + font-size: 29px; + text-transform: uppercase; + letter-spacing: 3px; + line-height: 1.2em; + padding: 62px 10px 20px; + white-space: normal; +} + +#kc-content { + width: 100%; +} + +#kc-attempted-username{ + font-size: 20px; + font-family:inherit; + font-weight: normal; + padding-right:10px; +} + +#kc-username{ + text-align: center; +} + +#kc-webauthn-settings-form{ + padding-top:8px; +} + +/* #kc-content-wrapper { + overflow-y: hidden; +} */ + +#kc-info { + padding-bottom: 200px; + margin-bottom: -200px; +} + +#kc-info-wrapper { + font-size: 13px; +} + +#kc-form-options span { + display: block; +} + +#kc-form-options .checkbox { + margin-top: 0; + color: #72767b; +} + +#kc-terms-text { + margin-bottom: 20px; +} + +#kc-registration { + margin-bottom: 15px; +} + +/* TOTP */ + +.subtitle { + text-align: right; + margin-top: 30px; + color: #909090; +} + +.required { + color: #CB2915; +} + +ol#kc-totp-settings { + margin: 0; + padding-left: 20px; +} + +ul#kc-totp-supported-apps { + margin-bottom: 10px; +} + +#kc-totp-secret-qr-code { + max-width:150px; + max-height:150px; +} + +#kc-totp-secret-key { + background-color: #fff; + color: #333333; + font-size: 16px; + padding: 10px 0; +} + +/* OAuth */ + +#kc-oauth h3 { + margin-top: 0; +} + +#kc-oauth ul { + list-style: none; + padding: 0; + margin: 0; +} + +#kc-oauth ul li { + border-top: 1px solid rgba(255, 255, 255, 0.1); + font-size: 12px; + padding: 10px 0; +} + +#kc-oauth ul li:first-of-type { + border-top: 0; +} + +#kc-oauth .kc-role { + display: inline-block; + width: 50%; +} + +/* Code */ +#kc-code textarea { + width: 100%; + height: 8em; +} + +/* Social */ + +#kc-social-providers ul { + padding: 0; +} + +#kc-social-providers li { + display: block; +} + +#kc-social-providers li:first-of-type { + margin-top: 0; +} + +.kc-login-tooltip{ + position:relative; + display: inline-block; +} + +.kc-login-tooltip .kc-tooltip-text{ + top:-3px; + left:160%; + background-color: black; + visibility: hidden; + color: #fff; + + min-width:130px; + text-align: center; + border-radius: 2px; + box-shadow:0 1px 8px rgba(0,0,0,0.6); + padding: 5px; + + position: absolute; + opacity:0; + transition:opacity 0.5s; +} + +/* Show tooltip */ +.kc-login-tooltip:hover .kc-tooltip-text { + visibility: visible; + opacity:0.7; +} + +/* Arrow for tooltip */ +.kc-login-tooltip .kc-tooltip-text::after { + content: " "; + position: absolute; + top: 15px; + right: 100%; + margin-top: -5px; + border-width: 5px; + border-style: solid; + border-color: transparent black transparent transparent; +} + +.zocial, +a.zocial { + width: 100%; + font-weight: normal; + font-size: 14px; + text-shadow: none; + border: 0; + background: #f5f5f5; + color: #72767b; + border-radius: 0; + white-space: normal; +} +.zocial:before { + border-right: 0; + margin-right: 0; +} +.zocial span:before { + padding: 7px 10px; + font-size: 14px; +} +.zocial:hover { + background: #ededed !important; +} + +.zocial.facebook, +.zocial.github, +.zocial.google, +.zocial.microsoft, +.zocial.stackoverflow, +.zocial.linkedin, +.zocial.twitter { + background-image: none; + border: 0; + + box-shadow: none; + text-shadow: none; +} + +/* Copy of zocial windows classes to be used for microsoft's social provider button */ +.zocial.microsoft:before{ content: "\f15d"; } +.zocial.stackoverflow:before{ color: inherit; } + + +@media (min-width: 768px) { + #kc-container-wrapper { + position: absolute; + width: 100%; + } + + .login-pf .container { + padding-right: 80px; + } + + #kc-locale { + position: relative; + text-align: right; + z-index: 9999; + } +} + +@media (max-width: 767px) { + + .login-pf body { + background: white; + } + + #kc-header { + padding-left: 15px; + padding-right: 15px; + float: none; + text-align: left; + } + + #kc-header-wrapper { + font-size: 16px; + font-weight: bold; + padding: 20px 60px 0 0; + color: #72767b; + letter-spacing: 0; + } + + div.kc-logo-text { + margin: 0; + width: 150px; + height: 32px; + background-size: 100%; + } + + #kc-form { + float: none; + } + + #kc-info-wrapper { + border-top: 1px solid rgba(255, 255, 255, 0.1); + margin-top: 15px; + padding-top: 15px; + padding-left: 0px; + padding-right: 15px; + } + + #kc-social-providers li { + display: block; + margin-right: 5px; + } + + .login-pf .container { + padding-top: 15px; + padding-bottom: 15px; + } + + #kc-locale { + position: absolute; + width: 200px; + top: 20px; + right: 20px; + text-align: right; + z-index: 9999; + } + + #kc-logo-wrapper { + background-size: 100px 21px; + height: 21px; + width: 100px; + margin: 20px 0 0 20px; + } + +} + +@media (min-height: 646px) { + #kc-container-wrapper { + bottom: 12%; + } +} + +@media (max-height: 645px) { + #kc-container-wrapper { + padding-top: 50px; + top: 20%; + } +} + +.card-pf form.form-actions .btn { + float: right; + margin-left: 10px; +} + +.login-pf-page .login-pf-brand { + margin-top: 20px; + max-width: 360px; + width: 40%; +} + +.card-pf { + background: #fff; + margin: 0 auto; + padding: 0 20px; + max-width: 500px; + border-top: 0; + box-shadow: 0px 0px 15px rgba(90, 90, 90, 0.459); + + -webkit-box-sizing: border-box; + box-sizing: border-box; + padding: 0; + color: rgba(0, 0, 0, 0.65); + font-size: 14px; + font-variant: tabular-nums; + line-height: 1.5715; + list-style: none; + -webkit-font-feature-settings: 'tnum'; + font-feature-settings: 'tnum'; + position: relative; + background: #fff; + border-radius: 2px; + -webkit-transition: all 0.3s; + transition: all 0.3s; +} + +/*tablet*/ +@media (max-width: 840px) { + .login-pf-page .card-pf{ + max-width: none; + margin-left: 20px; + margin-right: 20px; + padding: 20px 20px 30px 20px; + } +} +@media (max-width: 767px) { + .login-pf-page .card-pf{ + max-width: none; + margin-left: 0; + margin-right: 0; + padding-top: 0; + } + .card-pf.login-pf-accounts{ + max-width: none; + } +} + +.login-pf-page .login-pf-signup { + font-size: 15px; + color: #72767b; +} +#kc-content-wrapper .row { + margin-left: 0; + margin-right: 0; +} + +@media (min-width: 768px) { + + .login-pf-page .login-pf-social-section .login-pf-social-link:last-of-type { + margin-bottom: 0; + } +} + +.login-pf-page .login-pf-social-link { + margin-bottom: 25px; +} +.login-pf-page .login-pf-social-link a { + padding: 2px 0; + background-color: #5bab58; + color: white; + padding-top: 5px; + padding-bottom: 5px; +} + +.login-pf-page.login-pf-page-accounts { + margin-left: auto; + margin-right: auto; +} + +.login-pf-page .btn-primary { + margin-top: 0; +} + +.login-pf-page .list-view-pf .list-group-item { + border-bottom: 1px solid #ededed; +} + +.login-pf-page .list-view-pf-description { + width: 100%; +} + +.login-pf-page .card-pf{ + margin-bottom: 10px; +} + +#kc-form-login div.form-group:last-of-type, +#kc-register-form div.form-group:last-of-type, +#kc-update-profile-form div.form-group:last-of-type { + margin-bottom: 0px; +} + +#kc-back { + margin-top: 5px; +} + +form#kc-select-back-form div.login-pf-social-section { + padding-left: 0px; + border-left: 0px; +} + +#kc-form{ + display: flex; + flex-direction: column-reverse; +} +.login-pf-page .login-pf-social-section{ + width: 100%!important; + max-width: 100%!important; +} +.login-pf-page .login-pf-social-section:last-of-type{ + padding-left: 0px; + padding-right: 0px; + border-left: 0; +} +.login-pf-page .login-pf-social-section:first-of-type{ + padding-left: 40px; + padding-right: 40px; +} +#kc-social-providers{ + width: 100%!important; + padding-bottom: 40px; + border-bottom: 1px solid #d1d1d1; +} +#kc-form-wrapper{ + padding-top: 40px; +} +#login-username-button{ + position: absolute; + top: -10px; + z-index: 100; + padding: 0 20px; + background: white; + left: 50%; + transform: translate(-50%, 0); +} +#login-username-button:hover{ + color: #5bab58; + cursor: pointer; +} +#kc-form-login{ + display: none; +} \ No newline at end of file diff --git a/clusters/prod/apps/keycloak/theme/login/resources/img/HDC-logo.png b/clusters/prod/apps/keycloak/theme/login/resources/img/HDC-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..f360463c3587bf4c8f62069d9fe710d89b7f841e GIT binary patch literal 19646 zcmXtfV{|3o)AfyQClgI<+qUgw!ijC$oY=PQWG0v-6B{?ijcq;o{onPjUaPx5b)Tv_ zRcD`F`$Q=#N+H4H!vg>SBpGRORR91?`Rm;X7W(VnHC0El&Xe z>$p0-ydDpL*&b?~W0b3AF0QUk-20N0wECQY==7>p&+&S)$Mi4Y!LR?w-@e6dGD5QBeEZf2GN5=R0Z{>Jdn;`* z+=6hsDt3nOzX#2<73~7CLxy5aVWV={ zxh}i|)^7Tq3?ilf7*biR8SlJ)qG=M2Sq4Cfqgl^p&^`Wpmw5B#6)_OSw~UV%KSLup zz%t$4&vAMXM-i;UBcb0R_Eq{5^E85}z&o%iZ-6!={~bashm^bZaWl>5lq$T87;riZ z=NsOI)g#DO9rusg22nolPN~@+1XcwD`q6wX1~LDqTJZIZcx%B2p0QDgo!t%WaqL~o z%dP+uKu)vq_)#c;?y;YbQhNFH$K*|Mo-C7(KHlt{+lkzzdyl23Geo1UvsDAzemP2h zzEuTY?Bh4XA;^YIOLEIzpG^74}O*rO`teD~Ry4O>|^0Q`TgfBIn3OHEOOZ;m`gjPuzC}q$P-UI zfibjVU;IIHEGox=Vu?qOQt8k>UMQ*g*rwO*%YWU-%Fn*=ts~2BD1)%zj>rj{|7=1K z{A_X@v1uZN)^nZNO+4my%6(Lk8IkQ^s!HvGv3e6QOqs(~V7W|EU zPHnsv>hFTt+$A>K(RO%q8}ln&mRN73yB@-hDcK9YOpceKPp%>FPDu#%5-&n7f@vVh zYUCDAC??HQDXpK9)W`QsLkTk1XTZ3I9x7)_fCPSc=vk3}546oOr}vHLIly=zj} zT-?n^whfl1Ix`>h)fU?fW z!CO<<4WNNgzw8!Vm>ucak<(%BP*{{5*8iOw{)TBj*{Bxj60hKGSA z#w{5T85|{y`Ohwvsw2U?0g@Q(=MKL7wSMvke0^M!&N@2j$&IZ28pEumrbC>dmIb<><`4Wm z#6vtdNE$_C)mv_=OW_#&L{L3Ptelp#RBsO=}WlW#;P)HJuJ}@i3y% zuH|IjDkI;R=}2>%uK)p}eaw@ZV9vof6)G zzUEskIQ2I`V8N4f*A!})MotS<1=__3dP#_ud%keQudV}rX|CbVr)q|3tq=JDbPv-| zo87WVx?}~kbPCj>>{L?xQv4!rLV>$(oZFe=w7WDp+0S1mMa`U{NLwe@l`tSZC}n12 z+3(zjo$<{)4DcZyrhaIGERc59g)nO0&653bSw?f&^hGg@?GgSu^Fl6MaUmy){c{lN|MQKbZ;$(Fz$rf8OHk5tK6@Xu zWLbRI5`CCro>oAFbglu2DP5FAR6)Q^=6mU7?9|14&pNTGcA9h_s6W zUJOSA=0ERud4pQzKQa4GpKrec>jy~9C8pP|!Po$2=O6N!+nwa)jx&CZUv?9kNWYnH z0FKfLG(c^>=f{dUs0Uzp8+>KjU@(Z1U7U;~0`z05l%h1i!s=3ARBVCQV_ri*_#WW5 z4uFT4Igm)DpmWN*A(HH(2dWu83cNSB@(KDMz;GaUx=nHKm;Jw4!OS%V%|K#(1J8f= z>UK9%?a0{MFt&PA`QO6Yr|~to;$B5f5byjJLmgGt^T`iC>!!;~AT#`pb~EGC!~Qa9 zJGoIY$x9q>r1f!O`&iE=vFb0)-Qj~C3_cJm=3NsWU;ISlX?#l|7WxU zAeG1aUvevoCF_K0_jJ8u|02UWZ2LPfr2O;zley>Uet~*L4g81~Fjp^TM#6#I3kRrb zDbd=~U(0nB%Q2WZh?h@rIt{luHq1H2l49%gDYQakl>d{{4&T@uxpAzS8~gChed}G( z{ZyNdPZQI~Hr4Bg_vcMnfID%NPe`@q=@&$yK2#-WSBmhQVZ2{nU64($l67!hd`!iM z7V&T(*yyHX>G6>I;W~XvA--HlV%-BP&XjC=!b=6+dA>)8h_X}*4`|X^ZPE=(fk9#q zJCQw^V=?zSk0);)bvkID$d;tz?oBbUb;bxgm3I85ONru#KO<&Pkn=b-c-tHq8DK!L&t`(U z&mcl#9SNm^cr-Tx``9TgSXuJ#so{djq1(NRKzP!-#0}r* zo}Haw5d!lxX1GQy!c!T7qbX1tMrRv1aq;?n#^F*csP7ARF@uSRU`K#nW=>te_+`gd zie*GJ8Il@>7EdbwjTMjU^`QhFuV^REq@!u>rsL?IjrEDPUkE1I@m5=p#Y6AY%(W{8 zhaOd7&|P4bX~HCjgxO;WQ~4FD^Xu@|&pkdi@it`wQ)*+QKUm{&UM-5vM=m1-u4&I3$|+DDjW)|FoVN z7b>M{9A4nuLS=EdFs>TsAn>EWsv5_E2aXHF)6d~iHJNnyO&t-R{0FP)+{wK_B!BdW z)^KVTE9IW%ra2ux+CAi6dt8p*nZUt`AR=WAMcEyRilC_@i6$cLF3}RMD{6gTF37v3 zv@0`M6Nys44X>lX+=R%O42uNs>AdET(f5bRS?O3MeeY0Ad5H6An&u3P;ioMU&rAIX zj=67NanDV!h>)BOY)R1z*817)!+HWvqtlAE(fr(9-ca}&Hx$)lPTM+?z07H-V1s94 zT&6jix^keSc&uHZLnuDTp5yqPAmhK31JOZLAYT^{<7K=s6vO!M6SfFmW(HgklRtka zQNA%31_V|J1%i4w;N4~kolJ_|Lf+4^f+$}KTM&w)!$c0}SxivhbZ+inu6C(+-NJnY z+*o|(Z@O`7+M>#W+pb?kRhY8V42UP98T%(ya%E+Dm4h44%X6 z_Bg`MG47$v{l3{-RqFK$n9P3zmxkGW!C}GTiiVA2G2?n^G}ItTwU^WVzp?P zZt~2E?zzr++264Nb=^PJSLy|%e-7Vx?*0^j1D|D5Mg^kc-$gEM{Jlg+X;ie>vL5|Mo@|L>K;ok*g*lw4gf@jZ z(J~!YC}EY*gE8ox2o$k&jao05m?)0N(C-3x?>2(Hy!0PLH;;tA z_WGR!lhQ*6Ws-WEDUP7VI-Pu}9+6J2Z0nLcirE~7eOO04#r;Iy$1gdy<+yl&oXp5=!rd#hzrSegofzKPNbe+=LT`qmMqk|zz-(7AZu>>`cF zJa+*Z!nb^kfM`TRpU(ap)+{fT9&SmQdLhQo&yun!3LmJWwgZDMvc6PRysRHQ@To-_-UI0x^)5l zl6MVCbRo~Lnkg#rUEjDhtwIR=YwVqNHmAa)Ea5=tPzc84OLG-DP3^wEQOQe*NhT*V z$?DCizdwGC&V{9{n=JP&QqKad!l3Bl8ztv(VlwUf>H}no<+!0g)O1%-9uEO^OMM-| z;d~cWHpnYZ=C|6=C6V=hAr-aQLSjJti$(OAcc4FU&s7;;`ULp9Bg4fduj>b9kJ{VY z0@GQ5-ys1d7%G&e3Z8?WKgFG?-sUT$b2TFC9H2NwoI2R!H^sC9y_p z6Nfl0C8;l0Q|`Q|ZBw)%SB#ftD&-w{zm{?JETvYS=6nLeZCP`5a1hy-DuUVe4fal4 zRbvmHHhRC0j+f1{Cf08a#M0c>@n_nw+SGPN5YZnY{k(Wm`jGkGmM;L#!QHr%U0EAZ zJc7b^k`HP|=vA`ry;`Yb-_zT0;_B|#OiHQ>fBt%%Hu*jxO9@96dBo3@bHTm_h0z|e zW2=#46+>(5eV7fI=jE)E#M9I{CN=S<)e7oAlND$cdaIdYqS!Fs1d%kfo zv&jKdJKqIW(;aet)RJZ^&g=RtOD4{#r)*YMvAiQn%x03>eQN6&73y~FruKUO5q!0vCtuEmhM&6txyb4$ z8Dfmu@P9TE7T~6?xYZ)iPTS788(~~x3Qz8_GRStlt`_CFcl}(Lgh8NVFi6XpLa<3- zBD*GZ?x8fZ<7JK}gGhLz`F&qjH5mT3HYgd-Hr8s{CopRAZ`*qa-|hQa+>9cF3SBd%&_6z6U9{Nrhlp9}xQ9JUMoOP`4|@tb*n zI0tW?Nz3J~I2hZ{>nXr5;AY1tDODSC^+mauy{D8fPwG*|DO;I#GO~gBrqfb7f!2oL zrkb?F4%RnWAqvA9>L&V*k1JbG8F9B!@rVNFhS)txkDo#{Do9GU<;koKQ-^yO%SZ@1 ztzzI0cumjj;Dc;t^E*`{jA5I36b)3ixhml~bat13PMY`}Rji>Vsw{q$(v-FS1uiVn zH1a9Tz0tENSIJLx7-06%MhC2TJ|Eih$GaVLMN1=|mGZ-Qc_EDgigK(Al)!RpKcfYV z`X&Ymz0CT1t&U~WMJ^ciU)nBaqb9|)J^O6J*(B}3{gor=IAbj7a2KiHoiJmm&u5I+ z0IovT_PcKKZXATHVUrdRw^+vt)d>THVjiQRzK-h`m-2yw;IimZ=|)>QrDfFN+c(C$ zCcq6GL!j-_k1$NzGSky=k0PK^G0(dn*Q~*8`BjT@-5Qj$@nUttGk(Fc$Xl8GN9KZ_ zSMO>PLPUwE*P|)bCEISkazt$Fsw|Z270YN(cHE*b3N7)3dfm5u{w=B8FDFdC+*^U-QH&B&pxY#aDM0 zPttYfE+fBsUi9S5~z2t6QuxGqDRli;HD}z_WY)xrV%2q3si<%=LvYlEOUvv zYyBU!$mmw{JpQ)aB|}h^U3;v3+;{aAD{4KDL2_PW+C-zjLJE<`YIW=h+{rb~Z@Sjd zd>AwF7@&MfcK3S#a1)G!kg1NaaBxyjShf ziQ=MBc*squAtP0vU&c4_igX>OT+PX8;I)dlqQo08fp5}=_emuiU|`i{lskDYsigV> zl3NPTxke38rY=SMx&Sor0+g}L6HncZ(H-g1{^u@u=~;FQ>F!V*b{?HzsoM+70ACeL zhhX`1%0auV+>t8?jFi^}plt%Py_=l~$>n`A;iKa9AkAUToo;yiA`6JJAu$jrN6){D( zrm`84&q3u{xRY_0GPoW=_*zHp9x!&jCb7u?j?+c^y0AMnNA^_>MUFqyLpOyEqr+r4 zC{;7lC*P(j$gp^woq5gKiiD`S!Llh8mVaBPU{_V~(G#Q6ttd^_P5~-Yk?)nH-~x*$ zT_UP3U1Xq>1^GUKA9R#Iy~S46V+#|Q6GPK20z;~@Kp_7rs8M=P;%ETx5ucL12j6`9 z{ii!*N_iIaA)gv2e;v^-vz*VdFU;;C9%K=qA^$&+TA_IKl)Cyk_rTBb{yNC5;wLO% zZ5wwbgp_^`!q1;cY9W1lXa=EUx4Zl(VUAQXwu|pzEnYd5RzU$09o^6?ilezHAYvvF z@bo~?9F=n%w{#N7dvsZP$m{Ua^^n&*X&)v@F>xrjBJH$I8d~O5(w^e%KPtE#y@}JC#2uw5p=h_B^G3Sz*By$`*u7>qOI#f$PUS&@Ici z6xaIAgWS%4==3{g+C!sG{19P+22Yh4Q9yHL&xxl zrAEe|P8h)~I{yXOtwm74byi47+wx{q;*sIBp3&>5_j=^wLWmu<2ZMo5iOZfr#f9H~_@oxSrZw#5WH%?k0KcGV{WI*Ot2f$;PeLO^RcHo!2IUl#QZt9lrAw2ybq?Uhlgw8Fixfa^Ye z(!^_zn3eO)iSE0kVr5ba!=l0&ja22fl+p4|jbSi_S`v%Qn--cU0UaKCK**)6i5lmA zCn7Q|LCeYNam8!-7Nnue^7o6ZElzSFD8ifTlO1x%4+1COfK~QTCyaa#vy|le?1{R)g1G%N9^MI1>$FFJ4NR7c`viQSO+K`!9m6{QbtcJ(rT) z0Sw>NwOskak2k-_tv@5~GcwSW$9t`MpFT93MUQ<~k4ob3l=Vn=x%Wp+9kr%pcL*hS zw5#Yu-9I;`p7{2lCR0f^T|x;AREa&yd!Dk^28paEjbSEOxOyab zn23Ongo@28%_O!o4Rq=mBZ#O&IC|tf{LADGAF6$NeB~iZ4n#JTfv$>e7=wi<-Dn$( z0EHOKyc)WDl9niBYqw-{x?mX^4bop7tPa-R3RWh%3etj4Ttbz!(%Y(z#`*>oNjOL1 z=kHV)zpmy^hyS|2UHH2DBjojK2Y%4ZrNN1CQFQ+h9&jPo;m_P6BG|N~S*NyF(a_4xF5(R z7X}OHMF-wt@XuivUWS5H-I~*Ao?Z@<(5%&?0_{AfEiL*qMtu?Xkc1I>sS0pBqNKbs zIVAXRGxShOw5|Ou_Y}P1CwaN`-qV& zBr|e#$kg_@mHT;rS+eI#0%eqEp-hg%F(wlNTHP8AAFfuwBrNG<6r#j+Vl4i>$M?o# zlY9}zgn+OKWdm!le@gS=8ee^zh2xcQiwikwkTcBL4+*^hIX9)5bi;XQPvf#0&dnFO zf4Lec$lYm=4=M`ZO(g{%)7wuHPYRmc2_sa|*Q;K9v^hfjYWAKoI1l^q{t~d+ zL(!N&hI*-US=-h+7n&c4lC7gF_I8M%U}>Oms_;>HTj6ntUnIrO?{#Cd=ckCc2CU6m z1_h%U`g-}+!OZufd+r=9gtD1vS^xg0nrxkuoD**Kp;5Jnnl`-5ud7INx_7vGxMN5n z9D@q1xxj9}v2$)0QZqkaqdqB~4ieN|;n$q#k!>Z^N4#hwA)slzu@~_T6I$jSH4F0C zqO|;@^stiLYMiW+|u0uY$ zhn3d~%e708f4)sxsDXAZ^%941BTboDA01u!*Je@W^{=<^Ic=Co7^1g!Te)dE{up*w zrNTqov(GDMU97`*l(q#iSH_}oip{~g1qUyqV7y6@IFO7{|q&4P^KK?GnCSES0!8MWHq3MJ8tHv)cD93S|K5Ng{wWz_TC{r zvT2rYqH$e-T0}X^s8o+Lf~|qlg58$z*oaQ|_qV_pqgn=s*nu}F&#apYEZ9mJk(}f4 zN1sixvnq$6!_r5{O5%V}DJm{gh>?W>ZuUyX zfOG{cwW!$BkB`BfSr{>iI`wt`#ia|;#5}qc7G<8lOw{K)aD+GXd8D)8RcVi#{N=K; zYWQ$1@R@0xbrZnky8nRp97Sc)u}H(#p?O6VFPv-tr+(V0gPFTS)X~kp7{tbT>EXXm z*V?g9u&WsT?=wb#iTjjvzV~L2U)M&y-zfs_qX65n8J!Y)jy{xB$4L?rwjl)rI7UA} z1tW!mY&g<7sfLK?&-65v*la}V7xFKhbN5jM94guZGnz9TEJO0t(>T8Ajz4vIa%6Zq zJ}`GQr9{1uIjY#BJg9+(q`DAplm@s(Z8L#sD`Te_R(qOAX^mb6^2oKhL(~^LO<9ts z+~>QRihrbYN$Ti}4#gif@eUq{V~&eaT}f5uYaSmE7ihy)13~p3?DWE(Ijk z;J`1s!vuZXEfmZlAWN2U(>Uz*+=e#7f8AiL9$L5Rt@qS2vD*B}99tyizj{^t>eDI& z{%*)$gtqVz<~3t)qtdY)o$Wh->dv&no3a7-9O*TtpixM8KdsdZXj%K~ioos98oHHS zALGn_RxUXEH0^7e7X~}QEs*l4!L# z55LRq(g|ns`Kz=C`gLB3W{+=PJ4@?YUV48s+*MoS+#IU=h6<;Yq9|V&z(gll)R+8j zzE;>6Imf3lp0>@fG**^Oge_2WXEA#qdTMg#V|V+{Vr}FEGAh-ibnP!Rtn_87{`S3m zTL`P~AT0_1=SR%K`yZ%UPuT-hhq$l47_smUE5QJK{p2q!@WIr;fKjco??Q~OtCu+2 zzF@CJx_vI}c7aE5@cj921Aulc?=h_}Ee+izyEU9h_uAjsA{+9+=+!Dz0_$9ZC*tcO zUCH(*!BhA!l^3r586I;&R~OHrcRg2pKJ~vQ7Yfm5YLPG!2O|Ll$InlUDb(OIkR(J0 zqfu7i`e*kMAXbm6JLOmX%Qg~!>pEhtJnJOT6Kw8WC*SkFt*W_&#+F$>a~pjh$oOS2 zGpu)yR9fRSANAMNFS3wUF|62ee>7?p4>R3X!7k<%!Ja$K`{Cck#qe2_4d8mCHkCN= z$cXz3qs`wUee$Ry<+x+1A>O!h@c8?spdAqbTcOPDQswS4FPhza$p{ z#RElyo&4)P1%H`Yj}7d{BE*-~1qWS%IBxB#?Pw^!mp^nq5@Svu)#>C+rvd zUpEit%g<26oo$@rR;lg07jf zQZ-xlVSmIGx2nWg#E^PLXog)^q7^!qb1d#>iz)8Jg^$GY33U0Zr{a*9X;+I8G*$)? z>)O> zi#{Vv5%3U|EKIn-&F%k!o;h{%qv2SOjEri=L_QL^i{d-Tzadd=jhkOxoo!{JS7y3) z1=szRN0U3Hh=|sP7sfh#DKs6`<+33ea(y=^pA?@O9jA+G>=~_9;_rE{kZ||p9nQIC z=9HH*gER^QxGJg@lXbooS{|vM(o}>%<&u;VTAxpjK9*_;V&9D*<2^pGZCKFZwYcSk zy6wy_`qGVtpIwk2Xylvj(`w@Mey{hhz>BJBOk9QuOZ~B)$z`lPQ#Flfp!3_ze+_sF z`CTT#Kkuh>ogRq)xOyc}{Sbdwx-&kZ=1Yjb?d4yLCR7&Bru0jeTiyzZ+n0%4e0*zI?6O@ z6gJs0KC{fKgABG5j3dc}-FT7Tox#au1lhReO;^i2{xg+aQY zX$3Q&D2+Oqj!$M21`XF>=g)gw25A;Ti$DH_c&sUh!jf=Hv4c!Stkwt0~6{KJgqsK z?u3ML2M(atO65#9$kD^dg&5EQAEN0RW*&DkN!4ykT!M>iw+~PG?TvLmJYr6moL5bY zqN_ahkGL$U*Z60b+uIOG%$p1GdXbtu8PpO!^g2OCTbct8MZ_z8YX3Otr)2ZB=f)LQ zUw?fa=ODX_fTYBj`uHQ$ z^|1yeYQ`c}iFbyJ}c4=9)HC!`8X$eP>*<7fk1b7o&QjEW=6MW- zp*%YthYk8d+xg^tR#a_x!m2iBWKTEQ#DoHOO$rfsTF{~(fM@|1PA$|_TQOLwO=>i5 z7_|Y6uH==VrKmb}6#A(@c-YO4(LGQysZm2@o)Z%DLJ8*nk1h5c(fBI(`t^(qzg8k) z((>1|M zK(F2B(fp%KEI=;G2u zGme#9$h!KL&CH1zPF)CR0m&xVM`_N$CstBVG#D-uwjTu(7!b8+ ziF$K?^Q3j2hCN`FavIM67rfF{1U5_Ilb!VH&7$kUkvO5}zW@(p&;IVO-7P=FqiGjs zSP|C9S9w-|VCsPsy;Fl#aljKVWhoTgLC@sCf0)Aure|yt{l%HNXu|73Dg;B(I2a)_ ze6CIMAi^M^eImpV>v0(ZLA(CxG`PNaOreE`<6K?#Nqm}C8(IezO33t3tDrN~kf-73 ze$(-fI9~@1cFm*HBi16*ATtFiexD(W!vBlH#FOLiENkbfM*DEHNjd6FTT2_|nC+$K z2`C=r$1m9_1smoNOO*ih;V$O61KzKlPu;@hP;}|+2yAE_DG4tn{-9&J9`Yo`n|_sM zzJ%Yh_8bo(>N9sI++als*1L@`Bm%WCAU>2Cm2D16Kz&!thR@q(RlC=~hwK3BuV8zx zIrVap8XIU^LU7XF5|`J#+$WrX9s;W86xVU#7fXu7I)wPn0FHc_d*h~SpXB7MSzhY3 z%+QDNZ1GnidaEi1dqRR;9*iFZzc`fJgCR}K;7Eyad@LTihi;YpI-`pI&Z);^%(_Xh z&TSy=M1o0A-2Gwe)8ASPy%ARxcj5=aUov++-S`uwq5e9eDff0hVRf-}v4dF%y5rsZ zAvP+`SHU0f1b2gvU($JhOoHP#jR-C!^?PvVq45yhoJ!qSg85N3NUi^cR^>L!(rRq- zrEiSUMAJ^SQDbRJM*|(OYsLE|oSLZ)OFov&2&kP5LYlzN^D5a%sy9I}oHN-2rbbc!~+!#<`s; z5f0n%WS5>S3K_0hqI7?Ka%?oUwisKH%QAhn1O7u3C#FAFR zuzm5S@_8zcdPo`1^94*FJ*}A$>C!>Xn2M){U;WBe?h8JBTc=;PQ1DsRlOt5C%yZDI|8d4IKPz$S!Mns0?~$G7fvza^6} zdZ#sMIP>hkCe4&q;i&adA>w>-3~vemlA;}#OK)#{4_+*~_&M$85=v^VR!v>r(yHcT z^9gG+)4oxaPRCt((4BbO!F%19VjCZ@RjLyvg>JHQIl`lw$UJk(Eoz;{y;Dl+%fw}? zDTScm?t>LW|3#L%*(LsGNZZ@@v?8Txsfqq~D}F z*%~-r#$${{kk8H9q_&b`tBYk0#6^J01m$}Cur*6WlIxx*NFVDgn}29e>&?l3nY-xU z2%L(rBuR}c9IA(L7m|80pKqTTTmYk6s#@L}iXy)&4}b8qY4G;<^iOrHWoYK6eZe@^ zsM12Bv6aUfla93G>Bpa%7c#^;`1HIIEofwE$27?r0qq@R#NuH8NE*Zgy-2q#xGQNj zJ=tBRb1ZVjj|8r`GTcc%`&4HHH~(Gjbf%BrspuDf#4n+|dxq_?mXfce=*X_3rm@7* zZ>9F$1Poq{uW!5PWl2dszfEy+GE&;Ahq3)cUVu0EYHI3G{^U=-*dkUFHS=cPwc#BO z;a4#tTaF>rYNgvbV>{TCX3ii|FC*?BhOM2dEOsJ@SYP>4AhumIQr0(T5UFBPx&G^3 zfHLN7c8p4Bbrk{#yPnB-%4j?JdZFKcdv>N;;{1EF$@)GJ^Aw!{?ofx~Lfpn?9ltzg zQKC_w;6I_~01VO5NomyCA(}Ex{K1HLzf2CMLsC28R zO4mJXM-fWdQ~&?1MKO#L5r#=6;RWR@^HT4oel}wkHC<6ge3P}CzrrmqsI0V7(*}h+ z7kCqg6}*JdG@dtpNB*g|VZgE=oO>h8YY7MExd-#y*{Z)8|0I{tXdOh0tzkH0rPwvp zKH0~QN*#;EvL5;kb|L=nI~IKy4rNb? zU$wQz<2w(c#j@od;f6kA&R11SSq=STw6_Z$1-&F{n58XaUE<}f*{{^CcYK?o8$$;bptm4b0lwsL{)Ys7 zr=v8rCt0Wnd*TQD#RW6v<0AL4(s38h0w~*!xJNRTt&z|tnu(;eR_L?qBhVeMYW8t7 z%iyU&>YcM2G;nDe@FvlvsTXpM0lwLI%3=Nz=v zc?L`H81RWb5$y%9@l2_J_GLK9s#$&takKPagpCzJQo)r3`0$R%RUu-b&THB@`! zMNrhauE+}{6wp_Gvd9b|Bc_m2w}%TH8I2@xrKwhI1>GcSonkhrYrSRArPcdPg4WNV zO_@*NrGOf_GE5lBd&qJR$BTyXxT&vObAii`| zPCK!uTuW&aG-BjTI*_&ToL=>c9`i|%EJCz+u9{wR-`RP+Ac z?n0IJ(!uX$h%DaBK#uXbt;zo)=`Bru`98(giX#sPt{NizrY>SiRqtdKYm+Kzt)7}2 z8n{5t>dsx&5!oR`Q${c=ybOkPmlr6x=fT;uH;%{e8c;HcAl%g;+Uu@aX+h*$-Xqr= z8t!W!(UHB#GCTRVYSMK5DDPOajW@xZC;(jre@uy9$wJR^Jtnnw$z|Uqm*<|xv-)3( zm{sE1!79>2q>pgfVka7T=^gK_ZVd5sH*_iz$Di{b=4Ok#c+pS=N3|-D8C;o1T`9E1 z9XL0HQXJY#d>{vluaGS>`=5U8CG-;5$G8SN_i`S<49~ZzJv@93`FQ!@YD2!0yl)7- zWimx5eF-DPLE1C*A$}G=B=mKIBgy_%pgUtC(9vmBXjM&xnx|$lTBnL8T3e)kX9lJ_ zzYQ~#DcL!-MAQBm;8as;%10w!FhWmKiYfgd6LLD6?2fE8T`4*~ zoTxW~fh!;wH}DSn)>}vhBT3f+)C8@%p2WaZbd&NJnpK zd*?!7K`)NKUtwFL(|a7gNm~i>_TMzG)N*KjS4HmTRuJ(x<5@ku|@-jx@i@|gtGH`W4}Z! zv;I-N?4}m^qR+KIAIfE(T&3}?N#ccvk`6^FG0p0-cq^~pO>|z|SDxI{A z)ma8@0WN=nW1k&o$G&#TMoo zS(t>fqioJ6jERvF%3qzQL(csr4X0lrCyS|Ff7)aXTH9rD0@c;37j&Dnk>-l=+SF!f z34NL6QT8L81MhiYz`U~E%4rL~hxwD74`Gn2kB1yn$ks@Y0n@$j;2@6&7s!-i0!PN} z_tGwv_{i@;uh;LPAEKYC2fLI7Ss(MDCgqO-bs>WVt#SVRfG)XNd=2d-4`Ny0-U;b1 z`3ImPNr#||8rG8m<{$R(REEWKjHXxs1OWcQ$3>)9g-oBnMR2gSO0&i`jPJ1*iu&8< zUXs%;7tt~LwtBK~B9VNd7SuKE-*6F{M=ZCQXT;J|bshK`z9b zToIT>x?pv_YS-c^&Erh0M%l-5qJT7=Y&~{%fq(FH@z?;XsdEJZehd;=i%pDBoO3Xd zH`J0eyiwv(O>@+`S$iBZ>=VLx2Z%TREurm9lT3j4(?=g+U>7ukr)%H_9L6E093 zd?p;!2X=zYtXvrm_z^xIgS;t1{tjSWAPZt6vU1E8nD-*4Q%V-KWQ%mY$X~D-B zA0kFfEPMEbOyL$~6HAzamJ|8SnGlmQs&y^M zLN7=MHbT9$Ujda@7KF)g2l9O@Nz1|`dX_v7B8j?X`JRY1Dq!UyM(&Uf91tSjtyk7+ zng-YhvOA@95VEOv2Ln01l0rr*8xxbn87FjBgf=FSSYBH`81_{M(9|tm|H+^M$~+-3 z7vERxKYu7fgAxPH?+1KxjDK<|pPaGN>xH+T>U%G-(e-mP1e+QVyO@RU4;p80jd$HT`o_maW>vSwX(xH%< z9D!yB(34L$@4bOSf1(2AS4E&t?PY}^R+t)a?HWW#>D=k{vgCxOJGzU=9O*8j`+e51|V>}v=EHF#g4uB3fquZHW-A8Diu_?=-$5|&nFihaECoaPo&96H_?fCHUQiRu)*-i7 zX@&ISY3vcH;3{a?eh+XWfS(CeyU;@JEi#5XyS)+7%#*Z`7>{RY1#qzYREdsaRD)Cd zqV8O#w&jB{yUjoqG@X+`8u~vd@81&#w3>6ca*nyG{%adzcNI|fj9YA}m-c}>ckB2> z2nb(XWogR|le^&;i_s}PUC=GP(A6nj0ei{PEp^Y(tgP}bmAZC+tbVaM<`9c35&PM; zhnm%n>rwqXW)Ig#O_4wSp?SK&AM$SQjl=}fROu!O4_4KoE&xe;Dcxf6VBy;!ady~C zg?acv@zpM~yR{qqZ2qNz8)Sa~w0T@S@sQrzi-OJ)J&y38Or55_cG}GW&ibOR(ouLIfLIh7{G(uvmzlVpmtyy?r-0qWrfat)UePPLnYI} zkx(!=iUDU(vGpb#W|%Lu^4S`JuFrOBN`vXq8#<6)PG)?2EZ`ASJM_}VXNw+M+3G%I zOF$fmaei}4B;HO7W>nO#iyZj-72bD5cA<|r(5D6PrbpYKPBhjE90s#KoBAM~5rJav zAabG&YVtsT4E{0oVfLd}9wAl320XsBb7>atBkd}bB@B_m^?W9s#Gp#U+f<@fRUsy` z;{O7v3s&^AN-t2ZwG2!TG2_eA+2H57{i!FO!)p0=xxqz9m(gQ*dyuM$CxASCHURA$ z1gQt}PD|rRb9qURsNzz=QCE{U+%`Xn=wTK>!~* zmDA9y3JajC4Nbm9-S{EO9%ujsoNW0i-6r6r;}cXChZWhe;yLADSabYtO<*s1O5U<$DF8)FH_(!Y z)+ky+K;OHI0#tf)df?am=5|I4bSi!BmopX2uOS)+rHPTM^+;SOwYigXI;XIDVaYqJ zBFx%ZrA8P%mNd3XBTz7nobSV=xm9{=%GfAZEA2DqfsPw2@@;8{(7NMlq9O<{u#3w9ai2v zmR0Jng;naI)+$}Us8xDN3#;_>(yUSd(gSI>j~MMdcFSlx>qO?+LHL?Yj`MJASqkL_ z!>1atS}ikdA4QkOgK~VC`NuO)4MXP>^x9v@ztUR_98OK6&BD{9)7#%f&!hrCjm)S} zOH(zWCaDZix$ZQJ=zZ`Zz?!QK%r~JP+8xTCby*NMeDH9mV8FN>l&$jf5+U1u{Dt|? zmJ6UT{!s<|p2;oWbX@@|@Ao4;j&eBDYU8cHl*L}kJ}~on>bO9(> zivI@60&;-aLk9{x7V5ftqCU$5`D^s$1}MtuHKoeYqX>4hz@-3GE_EO<_Ezl-d-)}< z89?FK7)t&xu!L`!*Ft4_=-zlrM^)c2M_sz-Up2By16S564M$t0R}`$$Eh}o3&Zmvq z;aq=ZEt41BVj; zY1iH~%fGy-p&d)rK!MivR~`CyJ0ae01-%n-IL+&QGl-VC5&(r^N7$d!Y+Sxg<`OK> zdZfc93UKd3>rycwU*Yp2I%vK&0EO9C;P(ur>-^%SLu~$7Ua0dcmmg~kgzx-dc`eYF z`ma#s4q&HFS<^VU9dU^O-HrN;hg5&Tm|oW!pwjqIpVW_lH7Z2((2XH^!yOY-yubRS zJpH4}LdXNKd?Fv6HOdGw$@T(dG@uMkKc`S=0o97U& zR+*&jny!~`$9l>XR%Ki^ly}@It07XAY@&|{$*aQ~VcXCh> zgfvA``dUAE%+4$=u5}a?F{$7`({mbPfYW_3e22T73PH2NELg{t1649n^pP}q?%ZzD$+(@ zM~OXOZg|De^e8X4p32;vZ1V)ZwkO%ZkKbh$s+mK{pTX|hv=71#1M0u<#ydMQ`HYpbmJVZ5(jTYy4f%zxg6wG5@~cb*?1kSkZZhlW7Pl9O{xBfPYdIM~cpH zg5g>Vuax@_P&(-xgt7ExU^ydJDNG~9xQD(}=pjGvg?|sR!ll;%6unsxsUXwTN0$sx zIR50F_;0`Q$Z-9wN9we4?)<#7=~Y>!s9sE(Sf!y$u}UxKY?Z1>8=VU;Nuu@NAH2LX z`S>qay=7xL04n!fBYIW~j1g3PSfufuGjhkw2{|Btg<9s4&_VW6#|O$`1wE{5zA~4_ z&dM@qUcO%ck)g@|!0;Rd9;nz-sfQW<4xTr(c$N#G@Zm?YVPt{$QY+9C#;l@_J&cZV zL|fc&ziHqDjhUw_=EnSqd6pFiQ(&8^JFVD2JflcLxnvSZWTXM?xs^6d(&j ziWK8?dtitW{GyXvi1t!h0F~~EWR=NfikIPU(qMjgJz)r&wB#V_eTHVSNcNF5D`{T3 zkw9;}(lr*MJ@_yNSmvn2*~!09dr>Qpc+RB&o7cnOOh7C$`NNWk`W*~pC&>f6pUHm4d7*yq zilMQgvz9i<9&7+i?5w6RtpTda3%~d*#Y&AtS&|aD$DJq(0fSZ*c)(>@N)q%Jjr^RQ zF+<5<6oD-KsTLiYC0!B7g396fa?r)M>Qs&rN>@$_4Ka*U;oLJx>1j}iu)B=(a zF#b+2lryiCV^@_xsGQL@sAy4}RVe*^0#sx@AzvWQsGpawg}6NM-V9W8%9L59sajj5 z3!P3YV3h`YNtRY(Z)sL3Sl;&mkjCTU><^j5+5LRg_h2k%Dkofu$@Q-nN?!MB<=}8Q zq}e0VKyTEt3|bG;)I*b>L#qYR1(g75jD-9k@I^a9m7af}Je_g} z`b%Pg!$%e$I-^wnE)+@@QmbzM*e4VrXO@MA0R$wXrAn_AI9H|f8kTCrU|B3rbasNlA7XVcDJ81$&kJ7bhMrx^|;!hh_=83}N|KYb@_&@#@R(86b9`$>V)ItDK(eFKy z+JRLn1~3%^t5gg?Dh410f%)-bV3mRyEB@3Q&S)_JsTf$LVgOSxBg6otV1|i5gVR4$ z{CEElFoVIoC;nVIgTw%&UADxa=m08%h7i-A=t23Dzfi*&k(f#azk zSf%a2DlIpOI>!MpjWUTjbKWHO%r^7rGrwN-eH>sJ2Om)RlJY(E8@vBtep}U{FVbZ; z%rgfMt0qu||9e=@6R6Xnm(j@nkl57atl6o} zfX9|ElmDb#9{^rk={aL0BtUOHyhf^uv%oK$H3p_eP%c2U=6Ma!;@`nB|5N#OEblm! zOJHc!V%s9u6Iz?lxumF5ReOJ17(41WD#UGk}{x{|NrNPZtn(KBVQ8a7tFxMF0H9! zZkv;vw`S|MW5-XNJb&T(ji;~PeE9VF%hw;BsYz`>rHo15?k)Gs_^#gD*z?rKZ$Izwse0bB4!FJK4rA8IiPxK-cPk3t|NC>( zf1|X+cS?=#-Q4h|jQifqA77UT|4%;CSeg_i8KmWqJ82u!R^Lf!65HQenVgibjpI(9 T8{g~!w2#5l)z4*}Q$iB}Q|phQ literal 0 HcmV?d00001 diff --git a/clusters/prod/apps/keycloak/theme/login/resources/img/favicon.ico b/clusters/prod/apps/keycloak/theme/login/resources/img/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..48188dedaaed851f39dcf435d3bde8e0dce753fd GIT binary patch literal 627 zcmV-(0*w8MP)fnm`u?`7EDZT zU@#004h&*6qDT$3A+1HARIlxQw0Gw~Fqjw}@R{Bx@00f{&kMVRw3Fz%ejPaV7yTE* zFfRVFfUfKNfww^PA5AYAhLPDOVn^Z@5Uq#8eCj{K^1uiH-%|sWPn^Z+JGia8uj_ip zZxduPnQOLfKTs5fNvoYXN5LH&;bDG;iSDDcboJ0G%bfWzLoC#S@B5!sRW1H1Fh4(U zCK8G8Z0;+U=JG_6dl~BMWvaKyqfNox4TZyDm6NS?PHPsf>mtkY)(X?p)044SEDSKQ zTqY7~#q8?iUXO?Cx}0ybNC=-sv&nO(ja);Zsw$GSbv}_!r^h5o;>pJq=GIKi)BxwS zIvtLQX_~mM%k55!1cRzr-Bk6$hn3YADC!p(StiU*sn$CQ!;;MuB0(GzS&ySNfE*{22sK>&cF zD98ZA!^0c7Ty9jX6mNc<6~^+fSY63eDwURie5q8*?sVcL zaG@TFvod&`Xu~G?W|nd+MRD*L$@~KG!VpbFH`>7=NJimT8HPR1b>!7GRq%%D)H?|Wo% zC@Pq9$n!u*^5*>k`A#3+kZfF(`zJ152&b=J2><{9!0hZbO_;oR8NR%F9kLkE@5Z(} z|9X+Rqj^|(^f2UQ`B&qbstQ}zSHs@7Mx_7%0O*T*Sywa1;J+C|`oj73Q8;qyTvEs3fk{(-JWjQ|4!;N?Hk9Wu}T8~06?E)T~0nO z$|9aN^E2gq`rLgk&kqde>9h9ceYG2J7l(CKD-8eu0DaQs4ti*V_8>1|L40001@p8^a34>6V-IeVqb00000NkvXXu0mjf D@HyfE literal 0 HcmV?d00001 diff --git a/clusters/prod/apps/keycloak/theme/login/resources/img/feedback-error-sign.png b/clusters/prod/apps/keycloak/theme/login/resources/img/feedback-error-sign.png new file mode 100644 index 0000000000000000000000000000000000000000..0dd500445d7249ddd1b1dea7dc79d5e2f75cf223 GIT binary patch literal 343 zcmV-d0jU0oP)Rb^f-HfiA|a#ZS&BN>m{U!Bf9XBlP=AUsPhep z0;u>nQCxb~G6c;4tOM)aDVebC2LF?__!*$Y%|S5$e;ZcIdR+o#XrlC>L#@3RFZO+y z4ac0_$8S?_3L#)o1cV>wsb|~_2qr}-{Xy0sk1ddFrS2NjYBy!#lv@hhf%T~P1a2ls zg9cR!$Y8{U0euD81vt(&PyxuVl0?xy+oT=qfZ( zU<;qzo;L~X{8Dl*OuQ;g6C?jcvTY4MH)UgY@3N+I%Y&B-aM#Gmz4*K9-@h9B>+j#Q z@1I{-{^;BJ=zrF!cK!}Pr!g=bh|HcT>Z=m}wDHmbyC0nDaV#gVm&uycGotW zpv;r|b9e9Fzj)!!_FHF``Wx|qb*+1{*5LE=)%xG0rlsBtdT?-dwR{mlor@ArQ!T)1>sQ|BRGRy6pUXk5Eq%Jj zt#hDSUl#1`1Hp4rtdp{v4HrVu6{1-oD!M}vd literal 0 HcmV?d00001 diff --git a/clusters/prod/apps/keycloak/theme/login/resources/img/feedback-success-sign.png b/clusters/prod/apps/keycloak/theme/login/resources/img/feedback-success-sign.png new file mode 100644 index 0000000000000000000000000000000000000000..640bd71cab7bdfc7a8adcf28ffaf6db736a1c008 GIT binary patch literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^f*{Pn1|+R>-G2comSQK*5Dp-y;YjHK@;M7UB8!3Q zuY)k7lg8`{prB-lYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&3=E9oo-U3d z7QI&|@AYC16lmSQ(|nzi6W2vA0hQiX{shsj9%4*QVx6m7=T11}S0g4pTOrBJ(L2zj zqqUbmAu&;Tnwis)`FE;yFXbH4TM_-V#F%}bS9C7N)nccy^_%s?ydQOE$UA=bQASGdP zUS5*^?Y}eFBd=Y%8?p303zx;a-hVOO%tz8T)jmnDmV3_E#n4nO@i}MX#=5_=rbOm! z*!s=OFZw-8%CQyiuZC^?Jc-4l;#4*gQu&X%Q~loCIB2_ BsrLW? literal 0 HcmV?d00001 diff --git a/clusters/prod/apps/keycloak/theme/login/resources/img/feedback-warning-arrow-down.png b/clusters/prod/apps/keycloak/theme/login/resources/img/feedback-warning-arrow-down.png new file mode 100644 index 0000000000000000000000000000000000000000..6f2d9d2aeb1c1461767988a042aae50492d454bc GIT binary patch literal 513 zcmV+c0{;DpP)pbFH`>7=NJimT8HPR1b>!7GRq%%D)H?|Wo% zC@Pq9$n!u*^5*>k`A#3+kZfF(`zJ152&b=J2><{9!0hZbO_;oR8NR%F9kLkE@5Z(} z|9X+Rqj^|(^f2UQ`B&qbstQ}zSHs@7Mx_7%0O*T*Sywa1;J+C|`oj73Q8;qyTvEs3fk{(-JWjQ|4!;N?Hk9Wu}T8~06?E)T~0nO z$|9aN^E2gq`rLgk&kqde>9h9ceYG2J7l(CKD-8eu0DaQs4ti*V_8>1|L40001@p8^a34>6V-IeVqb00000NkvXXu0mjf D@HyfE literal 0 HcmV?d00001 diff --git a/clusters/prod/apps/keycloak/theme/login/resources/img/feedback-warning-sign.png b/clusters/prod/apps/keycloak/theme/login/resources/img/feedback-warning-sign.png new file mode 100644 index 0000000000000000000000000000000000000000..f9392a356fd3b383997c1ef289b48d02be96c351 GIT binary patch literal 646 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 literal 0 HcmV?d00001 diff --git a/clusters/prod/apps/keycloak/theme/login/resources/img/keycloak-logo-text.png b/clusters/prod/apps/keycloak/theme/login/resources/img/keycloak-logo-text.png new file mode 100644 index 0000000000000000000000000000000000000000..63f3b9f87deb2dee6172d44fbad7177064245101 GIT binary patch literal 19994 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 literal 0 HcmV?d00001 diff --git a/clusters/prod/apps/keycloak/theme/login/resources/img/lock.png b/clusters/prod/apps/keycloak/theme/login/resources/img/lock.png new file mode 100644 index 0000000000000000000000000000000000000000..697845b3dc99de65b86c71c93a33728a65f23c46 GIT binary patch literal 445 zcmV;u0Yd(XP)}4P@G8S`-z>NWC2TXwikV_3H~?2Z2BKAOEyN@IDK(r7q>i(Jvw`3Sa?1D~XtM8SGM<13%i1L=?}+Cb zXaJKqQVaLKZyGIGP~8D}JT+LQ4T^^}RRc0^k8I#zD-V7MeGNPU)ldfBfG*GjUPBnD z#N;P4@Gj*aAq;e-yysd(_ZymE<;nLppuKLHAb20p?LaNGfsW?g(V%{U`3W><@<6>Z nXMr+sk&^f?Fu?7apAP;38c1Dk>Q`me00000NkvXXu0mjfx3RB5 literal 0 HcmV?d00001 diff --git a/clusters/prod/apps/keycloak/theme/login/resources/img/user.png b/clusters/prod/apps/keycloak/theme/login/resources/img/user.png new file mode 100644 index 0000000000000000000000000000000000000000..c0f4d0a092195109f09dd25684c20623ca502aec GIT binary patch literal 683 zcmV;c0#yBpP)H0^R_BBkMJAU2>{|eqbd=IJ!Oq#k|EY0%aw#0n7q-WX;Mw`+FG}$Vm{j^@bQ2RfW!Y@QR9e zH^jgk?tRvI@ak|)`79~1CZ*6pxqiiTc2<;y1Mv-;*}!+XHhG9!G|Tl{h=GzaaMDBk z6woN~rw{{=RD8w5z*QAL^x*x6df*GDnf_qLaT@w1*N)}-5CZ_`aa(E~xR8)LAUQVw zT&TFkIF$#^3(R9aE$bR)>gk-Zyu`swxBC%+q%}t12-au-27y%%13!Tg;6RptmV^_) z3~)Sh|E(-NY&tR9PvZk{7FbL(5{Dw@GfOw%4WJ`|(*fM1vJuzFwtFhjg?Ws&$P?g@ z2frP7N~QU(tmIbA1hnJ{pw>g&qE@)JPDyH~;+`Ts2KMYg+@c24L$=;%8sR9gDak9i zc^-l$+%(-3jcsuo=k`(#;ue=x-CIeC?{NEgUycU$0zaho`|XmB;apDVC~omU)&KVz z^l_};&3qLMEW~MP1BX?-R0RVi6(6yI9>py+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&v(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!y||!y.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ve(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ye(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ve(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],y=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||y.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||y.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||y.push(".#.+[+~]"),e.querySelectorAll("\\\f"),y.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),y=y.length&&new RegExp(y.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),v=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&v(p,e)?-1:t==C||t.ownerDocument==p&&v(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!y||!y.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),v.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",v.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",v.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),v.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(v.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return B(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=_e(v.pixelPosition,function(e,t){if(t)return t=Be(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return B(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0 + + + + + + + + + <#if properties.meta?has_content> + <#list properties.meta?split(' ') as meta> + + + + ${msg("loginTitle",(realm.displayName!''))} + + <#if properties.styles?has_content> + <#list properties.styles?split(' ') as style> + + + + <#if properties.scripts?has_content> + <#list properties.scripts?split(' ') as script> + + + + <#if scripts??> + <#list scripts as script> + + + + + + +
+
+
${kcSanitize(msg("loginTitleHtml",(realm.displayNameHtml!'')))?no_esc}
+
+
+
+ <#if realm.internationalizationEnabled && locale.supported?size gt 1> +
+
+
+ ${locale.current} +
    + <#list locale.supported as l> +
  • ${l.label}
  • + +
+
+
+
+ + <#if !(auth?has_content && auth.showUsername() && !auth.showResetCredentials())> + <#if displayRequiredFields> +
+
+ * ${msg("requiredFields")} +
+
+

<#nested "header">

+
+
+ <#else> +

<#nested "header">

+ + <#else> + <#if displayRequiredFields> +
+
+ * ${msg("requiredFields")} +
+
+ <#nested "show-username"> +
+
+ + + + +
+
+
+
+ <#else> + <#nested "show-username"> +
+
+ + + + +
+
+ + +
+
+
+ + <#-- App-initiated actions should not see warning messages about the need to complete the action --> + <#-- during login. --> + <#if displayMessage && message?has_content && (message.type != 'warning' || !isAppInitiatedAction??)> +
+ <#if message.type = 'success'> + <#if message.type = 'warning'> + <#if message.type = 'error'> + <#if message.type = 'info'> + +
+ + + <#nested "form"> + + <#if auth?has_content && auth.showTryAnotherWayLink() && showAnotherWayIfPresent> +
class="${properties.kcContentWrapperClass!}"> +
class="${properties.kcFormSocialAccountContentClass!} ${properties.kcFormSocialAccountClass!}"> + +
+
+ + + <#if displayInfo> +
+
+ <#nested "info"> +
+
+ +
+
+ +
+
+ + + diff --git a/clusters/prod/apps/keycloak/theme/login/theme.properties b/clusters/prod/apps/keycloak/theme/login/theme.properties new file mode 100644 index 0000000..36a1d41 --- /dev/null +++ b/clusters/prod/apps/keycloak/theme/login/theme.properties @@ -0,0 +1,95 @@ +parent=base +import=common/keycloak + +styles=node_modules/patternfly/dist/css/patternfly.min.css node_modules/patternfly/dist/css/patternfly-additions.min.css lib/zocial/zocial.css css/login.css?v=3 +scripts=js/jquery.min.js js/login.js +meta=viewport==width=device-width,initial-scale=1 + +kcHtmlClass=login-pf +kcLoginClass=login-pf-page + +kcLogoLink=http://www.keycloak.org + +kcLogoClass=login-pf-brand + +kcContainerClass=container-fluid +kcContentClass=col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3 col-lg-6 col-lg-offset-3 +kcContentWrapperClass=row + +kcHeaderClass=login-pf-page-header +kcFeedbackAreaClass=col-md-12 +kcLocaleClass=col-xs-12 col-sm-1 +kcAlertIconClasserror=pficon pficon-error-circle-o + +kcFormAreaClass=col-sm-10 col-sm-offset-1 col-md-8 col-md-offset-2 col-lg-8 col-lg-offset-2 +kcFormCardClass=card-pf +kcFormCardAccountClass=login-pf-accounts +kcFormSocialAccountClass=login-pf-social-section +kcFormSocialAccountContentClass=col-xs-12 col-sm-6 +kcFormSocialAccountListClass=login-pf-social list-unstyled login-pf-social-all +kcFormSocialAccountDoubleListClass=login-pf-social-double-col +kcFormSocialAccountListLinkClass=login-pf-social-link +kcFormHeaderClass=login-pf-header + +kcFeedbackErrorIcon=pficon pficon-error-circle-o +kcFeedbackWarningIcon=pficon pficon-warning-triangle-o +kcFeedbackSuccessIcon=pficon pficon-ok +kcFeedbackInfoIcon=pficon pficon-info + +kcResetFlowIcon=pficon pficon-arrow fa-2x +kcWebAuthnKeyIcon=pficon pficon-key + +kcFormClass=form-horizontal +kcFormGroupClass=form-group +kcFormGroupErrorClass=has-error +kcLabelClass=control-label +kcLabelWrapperClass=col-xs-12 col-sm-12 col-md-12 col-lg-12 +kcInputClass=form-control +kcInputWrapperClass=col-xs-12 col-sm-12 col-md-12 col-lg-12 +kcFormOptionsClass=col-xs-12 col-sm-12 col-md-12 col-lg-12 +kcFormButtonsClass=col-xs-12 col-sm-12 col-md-12 col-lg-12 +kcFormSettingClass=login-pf-settings +kcTextareaClass=form-control +kcSignUpClass=login-pf-signup + + +kcInfoAreaClass=col-xs-12 col-sm-4 col-md-4 col-lg-5 details + +##### css classes for form buttons +# main class used for all buttons +kcButtonClass=btn +# classes defining priority of the button - primary or default (there is typically only one priority button for the form) +kcButtonPrimaryClass=btn-primary +kcButtonDefaultClass=btn-default +# classes defining size of the button +kcButtonLargeClass=btn-lg +kcButtonBlockClass=btn-block + +##### css classes for input +kcInputLargeClass=input-lg + +##### css classes for form accessability +kcSrOnlyClass=sr-only + +##### css classes for select-authenticator form +kcSelectAuthListClass=list-group list-view-pf +kcSelectAuthListItemClass=list-group-item list-view-pf-stacked +kcSelectAuthListItemInfoClass=list-view-pf-main-info +kcSelectAuthListItemLeftClass=list-view-pf-left +kcSelectAuthListItemBodyClass=list-view-pf-body +kcSelectAuthListItemDescriptionClass=list-view-pf-description +kcSelectAuthListItemHeadingClass=list-group-item-heading +kcSelectAuthListItemHelpTextClass=list-group-item-text + +##### css classes for the authenticators +kcAuthenticatorDefaultClass=fa list-view-pf-icon-lg +kcAuthenticatorPasswordClass=fa fa-unlock list-view-pf-icon-lg +kcAuthenticatorOTPClass=fa fa-mobile list-view-pf-icon-lg +kcAuthenticatorWebAuthnClass=fa fa-key list-view-pf-icon-lg +kcAuthenticatorWebAuthnPasswordlessClass=fa fa-key list-view-pf-icon-lg + +##### css classes for the OTP Login Form +kcSelectOTPListClass=card-pf card-pf-view card-pf-view-select card-pf-view-single-select +kcSelectOTPListItemClass=card-pf-body card-pf-top-element +kcAuthenticatorOtpCircleClass=fa fa-mobile card-pf-icon-circle +kcSelectOTPItemHeadingClass=card-pf-title text-center \ No newline at end of file diff --git a/clusters/prod/apps/keycloak/values.yaml b/clusters/prod/apps/keycloak/values.yaml new file mode 100644 index 0000000..8669c7a --- /dev/null +++ b/clusters/prod/apps/keycloak/values.yaml @@ -0,0 +1,135 @@ +keycloak: + global: + imagePullSecrets: + - docker-registry-secret + + image: + repository: hdc-services-external/bitnami/keycloak + + auth: + existingSecret: keycloak-credentials + passwordSecretKey: "admin-password" + adminUser: user + + service: + type: ClusterIP + + postgresql: + enabled: false + + externalDatabase: + host: "keycloak-postgresql.keycloak" + port: 5432 + user: bn_keycloak + database: bitnami_keycloak + existingSecret: keycloak-db-credentials + existingSecretPasswordKey: password + + extraEnvVars: + - name: KEYCLOAK_LOGLEVEL + value: INFO + - name: KEYCLOAK_PROXY_ADDRESS_FORWARDING + value: "true" + - name: JAVA_OPTS + value: >- + -Dkeycloak.profile.feature.scripts=enabled + -Dkeycloak.profile.feature.upload_scripts=enabled + -Dkeycloak.profile.feature.token_exchange=enabled + -Dkeycloak.adminUrl=https://iam.hdc.ebrains.eu + -Dkeycloak.frontendUrl=https://iam.hdc.ebrains.eu + + extraVolumes: + - name: antd + persistentVolumeClaim: + claimName: keycloak-antd + - name: theme-cm + configMap: + name: keycloak-antd-theme + + extraVolumeMounts: + - name: antd + mountPath: /opt/bitnami/keycloak/themes/keycloak-antd/ + + initContainers: + - name: download-plugins + image: n47w5524.c1.de1.container-registry.ovh.net/hdc-services-external/alpine:3.21 + imagePullPolicy: IfNotPresent + securityContext: + # Root required to chown downloaded plugins to uid 1001 (keycloak) + runAsUser: 0 + runAsGroup: 0 + allowPrivilegeEscalation: false + resources: + requests: + cpu: 100m + memory: 64Mi + limits: + cpu: 200m + memory: 128Mi + volumeMounts: + - mountPath: /opt/bitnami/keycloak/themes/keycloak-antd/ + name: antd + - mountPath: /tmp/theme + name: theme-cm + envFrom: + - secretRef: + name: github-access-token + command: ["/bin/sh", "-c"] + args: + - > + set -e; + THEME_DIR=/opt/bitnami/keycloak/themes/keycloak-antd; + echo "=== Installing theme files ===" && + mkdir -p $THEME_DIR/login/resources/css \ + $THEME_DIR/login/resources/js \ + $THEME_DIR/login/resources/img && + cp /tmp/theme/login.ftl $THEME_DIR/login/ && + cp /tmp/theme/template.ftl $THEME_DIR/login/ && + cp /tmp/theme/theme.properties $THEME_DIR/login/ && + cp /tmp/theme/login.css $THEME_DIR/login/resources/css/ && + cp /tmp/theme/jquery.min.js $THEME_DIR/login/resources/js/ && + cp /tmp/theme/login.js $THEME_DIR/login/resources/js/ && + cp /tmp/theme/error.png /tmp/theme/favicon.ico \ + /tmp/theme/feedback-error-arrow-down.png /tmp/theme/feedback-error-sign.png \ + /tmp/theme/feedback-success-arrow-down.png /tmp/theme/feedback-success-sign.png \ + /tmp/theme/feedback-warning-arrow-down.png /tmp/theme/feedback-warning-sign.png \ + /tmp/theme/HDC-logo.png /tmp/theme/keycloak-bg.png \ + /tmp/theme/keycloak-logo.png /tmp/theme/keycloak-logo-text.png \ + /tmp/theme/lock.png /tmp/theme/user.png \ + $THEME_DIR/login/resources/img/ && + echo "=== Downloading plugins ===" && + apk add --no-cache wget ca-certificates && + mkdir -p $THEME_DIR/plugins && + rm -fv $THEME_DIR/plugins/keycloak-last-login-* $THEME_DIR/plugins/keycloak-auth-require-group-* && + wget --header "Authorization: token $GITHUB_TOKEN" + https://maven.pkg.github.com/PilotDataPlatform/keycloak-extensions/org/indocresearch/pilot/keycloak-last-login/1.0.1/keycloak-last-login-1.0.1.jar + -P $THEME_DIR/plugins && + wget --header "Authorization: token $GITHUB_TOKEN" + https://maven.pkg.github.com/PilotDataPlatform/keycloak-extensions/com/github/thomasdarimont/keycloak/keycloak-auth-require-group/1.0.0/keycloak-auth-require-group-1.0.0.jar + -P $THEME_DIR/plugins && + chown -R 1001:1001 $THEME_DIR + + initdbScripts: + move_plugins.sh: | + #!/bin/bash + echo "Checking plugins folder..." + ls -trl /opt/bitnami/keycloak/themes/keycloak-antd/plugins + echo "Copying plugins to providers..." + cp -r /opt/bitnami/keycloak/themes/keycloak-antd/plugins/* /opt/bitnami/keycloak/providers/ + echo "Done!" + ls -trl /opt/bitnami/keycloak/providers + + ingress: + enabled: true + ingressClassName: nginx + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + nginx.ingress.kubernetes.io/proxy-buffer-size: "128k" + hostname: iam.hdc.ebrains.eu + pathType: Prefix + path: / + servicePort: http + tls: true + + logging: + level: INFO diff --git a/docs/vault-secrets.md b/docs/vault-secrets.md index 0aaeac1..02dd806 100644 --- a/docs/vault-secrets.md +++ b/docs/vault-secrets.md @@ -31,7 +31,8 @@ vault kv patch secret/postgresql -user-password=$(openssl rand -hex 24) vault kv put secret/keycloak \ admin-password=$(openssl rand -hex 24) \ postgres-password=$(openssl rand -hex 24) \ - keycloak-user-password=$(openssl rand -hex 24) + keycloak-user-password=$(openssl rand -hex 24) \ + github-token='' ``` ## Redis (`secret/redis`) @@ -76,7 +77,8 @@ vault kv put secret/docker-registry/ovh \ ```bash vault kv put secret/auth \ - keycloak-client-secret='' + keycloak-client-secret='' \ + freeipa-password='' ``` ## Kong (`secret/kong`) From 5e6c1ca9cd835c56c38810de4834c77772de6357 Mon Sep 17 00:00:00 2001 From: Antonio Cascais Date: Mon, 13 Apr 2026 10:33:33 +0200 Subject: [PATCH 2/2] feat: add prod apps for waves 8-9 (services, kong) 20 prod apps: metadata, project, dataset, dataops, notification, approval, kong-postgresql, kong, queue-{consumer,producer,socketio}, pipelinewatch, upload-{core,greenroom}, download-{core,greenroom}, search, xwiki, metadata-event-handler, kg-integration. Also cleans up stale TODOs in dev (bff-cli, kong-postgresql). SMTP relay still TBD (notification uses TODO-PROD-SMTP-RELAY placeholder). Vault secrets needed before merge: kong, xwiki, approval, kg-integration. --- clusters/dev/apps/bff-cli/values.yaml | 2 - .../dev/apps/kong-postgresql/application.yaml | 4 - clusters/prod/apps/approval/Chart.yaml | 7 + clusters/prod/apps/approval/application.yaml | 28 ++++ .../approval/templates/external-secret.yaml | 25 +++ clusters/prod/apps/approval/values.yaml | 88 +++++++++++ clusters/prod/apps/dataops/Chart.yaml | 7 + clusters/prod/apps/dataops/application.yaml | 28 ++++ .../dataops/templates/external-secret.yaml | 21 +++ clusters/prod/apps/dataops/values.yaml | 84 ++++++++++ clusters/prod/apps/dataset/Chart.yaml | 7 + clusters/prod/apps/dataset/application.yaml | 28 ++++ .../dataset/templates/external-secret.yaml | 36 +++++ clusters/prod/apps/dataset/values.yaml | 143 ++++++++++++++++++ clusters/prod/apps/download-core/Chart.yaml | 7 + .../prod/apps/download-core/application.yaml | 28 ++++ .../templates/external-secret.yaml | 29 ++++ clusters/prod/apps/download-core/values.yaml | 94 ++++++++++++ .../prod/apps/download-greenroom/Chart.yaml | 7 + .../apps/download-greenroom/application.yaml | 28 ++++ .../templates/external-secret.yaml | 29 ++++ .../prod/apps/download-greenroom/values.yaml | 94 ++++++++++++ clusters/prod/apps/kg-integration/Chart.yaml | 7 + .../prod/apps/kg-integration/application.yaml | 27 ++++ .../templates/external-secret.yaml | 21 +++ clusters/prod/apps/kg-integration/values.yaml | 73 +++++++++ clusters/prod/apps/kong-postgresql/Chart.yaml | 7 + .../apps/kong-postgresql/application.yaml | 28 ++++ .../templates/external-secret.yaml | 21 +++ .../prod/apps/kong-postgresql/values.yaml | 40 +++++ clusters/prod/apps/kong/Chart.yaml | 7 + clusters/prod/apps/kong/application.yaml | 28 ++++ clusters/prod/apps/kong/values.yaml | 76 ++++++++++ .../apps/metadata-event-handler/Chart.yaml | 7 + .../metadata-event-handler/application.yaml | 28 ++++ .../apps/metadata-event-handler/values.yaml | 57 +++++++ clusters/prod/apps/metadata/Chart.yaml | 7 + clusters/prod/apps/metadata/application.yaml | 28 ++++ .../metadata/templates/external-secret.yaml | 17 +++ clusters/prod/apps/metadata/values.yaml | 75 +++++++++ clusters/prod/apps/notification/Chart.yaml | 7 + .../prod/apps/notification/application.yaml | 28 ++++ .../templates/external-secret.yaml | 17 +++ clusters/prod/apps/notification/values.yaml | 78 ++++++++++ clusters/prod/apps/pipelinewatch/Chart.yaml | 7 + .../prod/apps/pipelinewatch/application.yaml | 28 ++++ .../apps/pipelinewatch/templates/rbac.yaml | 21 +++ clusters/prod/apps/pipelinewatch/values.yaml | 46 ++++++ clusters/prod/apps/project/Chart.yaml | 7 + clusters/prod/apps/project/application.yaml | 28 ++++ .../project/templates/external-secret.yaml | 25 +++ clusters/prod/apps/project/values.yaml | 102 +++++++++++++ clusters/prod/apps/queue-consumer/Chart.yaml | 7 + .../prod/apps/queue-consumer/application.yaml | 28 ++++ .../templates/external-secret.yaml | 33 ++++ .../apps/queue-consumer/templates/rbac.yaml | 23 +++ clusters/prod/apps/queue-consumer/values.yaml | 104 +++++++++++++ clusters/prod/apps/queue-producer/Chart.yaml | 7 + .../prod/apps/queue-producer/application.yaml | 27 ++++ .../templates/external-secret.yaml | 21 +++ clusters/prod/apps/queue-producer/values.yaml | 57 +++++++ clusters/prod/apps/queue-socketio/Chart.yaml | 7 + .../prod/apps/queue-socketio/application.yaml | 27 ++++ .../templates/external-secret.yaml | 21 +++ clusters/prod/apps/queue-socketio/values.yaml | 64 ++++++++ clusters/prod/apps/search/Chart.yaml | 7 + clusters/prod/apps/search/application.yaml | 28 ++++ clusters/prod/apps/search/values.yaml | 47 ++++++ clusters/prod/apps/upload-core/Chart.yaml | 7 + .../prod/apps/upload-core/application.yaml | 28 ++++ .../templates/external-secret.yaml | 25 +++ clusters/prod/apps/upload-core/values.yaml | 96 ++++++++++++ .../prod/apps/upload-greenroom/Chart.yaml | 7 + .../apps/upload-greenroom/application.yaml | 28 ++++ .../templates/external-secret.yaml | 25 +++ .../prod/apps/upload-greenroom/values.yaml | 96 ++++++++++++ clusters/prod/apps/xwiki/Chart.yaml | 7 + clusters/prod/apps/xwiki/application.yaml | 28 ++++ .../apps/xwiki/templates/external-secret.yaml | 42 +++++ clusters/prod/apps/xwiki/templates/pvc.yaml | 12 ++ clusters/prod/apps/xwiki/values.yaml | 80 ++++++++++ docs/vault-secrets.md | 11 ++ 82 files changed, 2766 insertions(+), 6 deletions(-) create mode 100644 clusters/prod/apps/approval/Chart.yaml create mode 100644 clusters/prod/apps/approval/application.yaml create mode 100644 clusters/prod/apps/approval/templates/external-secret.yaml create mode 100644 clusters/prod/apps/approval/values.yaml create mode 100644 clusters/prod/apps/dataops/Chart.yaml create mode 100644 clusters/prod/apps/dataops/application.yaml create mode 100644 clusters/prod/apps/dataops/templates/external-secret.yaml create mode 100644 clusters/prod/apps/dataops/values.yaml create mode 100644 clusters/prod/apps/dataset/Chart.yaml create mode 100644 clusters/prod/apps/dataset/application.yaml create mode 100644 clusters/prod/apps/dataset/templates/external-secret.yaml create mode 100644 clusters/prod/apps/dataset/values.yaml create mode 100644 clusters/prod/apps/download-core/Chart.yaml create mode 100644 clusters/prod/apps/download-core/application.yaml create mode 100644 clusters/prod/apps/download-core/templates/external-secret.yaml create mode 100644 clusters/prod/apps/download-core/values.yaml create mode 100644 clusters/prod/apps/download-greenroom/Chart.yaml create mode 100644 clusters/prod/apps/download-greenroom/application.yaml create mode 100644 clusters/prod/apps/download-greenroom/templates/external-secret.yaml create mode 100644 clusters/prod/apps/download-greenroom/values.yaml create mode 100644 clusters/prod/apps/kg-integration/Chart.yaml create mode 100644 clusters/prod/apps/kg-integration/application.yaml create mode 100644 clusters/prod/apps/kg-integration/templates/external-secret.yaml create mode 100644 clusters/prod/apps/kg-integration/values.yaml create mode 100644 clusters/prod/apps/kong-postgresql/Chart.yaml create mode 100644 clusters/prod/apps/kong-postgresql/application.yaml create mode 100644 clusters/prod/apps/kong-postgresql/templates/external-secret.yaml create mode 100644 clusters/prod/apps/kong-postgresql/values.yaml create mode 100644 clusters/prod/apps/kong/Chart.yaml create mode 100644 clusters/prod/apps/kong/application.yaml create mode 100644 clusters/prod/apps/kong/values.yaml create mode 100644 clusters/prod/apps/metadata-event-handler/Chart.yaml create mode 100644 clusters/prod/apps/metadata-event-handler/application.yaml create mode 100644 clusters/prod/apps/metadata-event-handler/values.yaml create mode 100644 clusters/prod/apps/metadata/Chart.yaml create mode 100644 clusters/prod/apps/metadata/application.yaml create mode 100644 clusters/prod/apps/metadata/templates/external-secret.yaml create mode 100644 clusters/prod/apps/metadata/values.yaml create mode 100644 clusters/prod/apps/notification/Chart.yaml create mode 100644 clusters/prod/apps/notification/application.yaml create mode 100644 clusters/prod/apps/notification/templates/external-secret.yaml create mode 100644 clusters/prod/apps/notification/values.yaml create mode 100644 clusters/prod/apps/pipelinewatch/Chart.yaml create mode 100644 clusters/prod/apps/pipelinewatch/application.yaml create mode 100644 clusters/prod/apps/pipelinewatch/templates/rbac.yaml create mode 100644 clusters/prod/apps/pipelinewatch/values.yaml create mode 100644 clusters/prod/apps/project/Chart.yaml create mode 100644 clusters/prod/apps/project/application.yaml create mode 100644 clusters/prod/apps/project/templates/external-secret.yaml create mode 100644 clusters/prod/apps/project/values.yaml create mode 100644 clusters/prod/apps/queue-consumer/Chart.yaml create mode 100644 clusters/prod/apps/queue-consumer/application.yaml create mode 100644 clusters/prod/apps/queue-consumer/templates/external-secret.yaml create mode 100644 clusters/prod/apps/queue-consumer/templates/rbac.yaml create mode 100644 clusters/prod/apps/queue-consumer/values.yaml create mode 100644 clusters/prod/apps/queue-producer/Chart.yaml create mode 100644 clusters/prod/apps/queue-producer/application.yaml create mode 100644 clusters/prod/apps/queue-producer/templates/external-secret.yaml create mode 100644 clusters/prod/apps/queue-producer/values.yaml create mode 100644 clusters/prod/apps/queue-socketio/Chart.yaml create mode 100644 clusters/prod/apps/queue-socketio/application.yaml create mode 100644 clusters/prod/apps/queue-socketio/templates/external-secret.yaml create mode 100644 clusters/prod/apps/queue-socketio/values.yaml create mode 100644 clusters/prod/apps/search/Chart.yaml create mode 100644 clusters/prod/apps/search/application.yaml create mode 100644 clusters/prod/apps/search/values.yaml create mode 100644 clusters/prod/apps/upload-core/Chart.yaml create mode 100644 clusters/prod/apps/upload-core/application.yaml create mode 100644 clusters/prod/apps/upload-core/templates/external-secret.yaml create mode 100644 clusters/prod/apps/upload-core/values.yaml create mode 100644 clusters/prod/apps/upload-greenroom/Chart.yaml create mode 100644 clusters/prod/apps/upload-greenroom/application.yaml create mode 100644 clusters/prod/apps/upload-greenroom/templates/external-secret.yaml create mode 100644 clusters/prod/apps/upload-greenroom/values.yaml create mode 100644 clusters/prod/apps/xwiki/Chart.yaml create mode 100644 clusters/prod/apps/xwiki/application.yaml create mode 100644 clusters/prod/apps/xwiki/templates/external-secret.yaml create mode 100644 clusters/prod/apps/xwiki/templates/pvc.yaml create mode 100644 clusters/prod/apps/xwiki/values.yaml diff --git a/clusters/dev/apps/bff-cli/values.yaml b/clusters/dev/apps/bff-cli/values.yaml index 20f16ed..a3102e5 100644 --- a/clusters/dev/apps/bff-cli/values.yaml +++ b/clusters/dev/apps/bff-cli/values.yaml @@ -48,7 +48,6 @@ bff-cli-service: CORE_ZONE_LABEL: "Core" GREEN_ZONE_LABEL: "Greenroom" - # TODO: fill when these services are deployed on OVH AUDIT_TRAIL_SERVICE: "" HPC_SERVICE: "" KG_SERVICE: "" @@ -59,7 +58,6 @@ bff-cli-service: # Guacamole CLI auth CLI_PUBLIC_KEY_PATH: "/var/run/secrets/guacamole/jwt-key.pub" - # TODO: replace with OVH pod CIDR when JupyterHub is deployed JUPYTER_IP_LOWER: "10.0.0.0" JUPYTER_IP_UPPER: "10.255.255.255" diff --git a/clusters/dev/apps/kong-postgresql/application.yaml b/clusters/dev/apps/kong-postgresql/application.yaml index 3c60a00..e3ba652 100644 --- a/clusters/dev/apps/kong-postgresql/application.yaml +++ b/clusters/dev/apps/kong-postgresql/application.yaml @@ -15,10 +15,6 @@ spec: helm: valueFiles: - ../../registry.yaml - # TODO: versions.yaml has pg_cron tag for main utility postgres only. - # Proper fix: add postgresql-standard key to versions.yaml with standard - # bitnami tag, then reference it here. For now, using chart default. - # - ../../versions.yaml - values.yaml destination: server: https://kubernetes.default.svc diff --git a/clusters/prod/apps/approval/Chart.yaml b/clusters/prod/apps/approval/Chart.yaml new file mode 100644 index 0000000..9fdb260 --- /dev/null +++ b/clusters/prod/apps/approval/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: approval +version: 0.1.0 +dependencies: + - name: approval-service + version: "0.3.1" + repository: https://pilotdataplatform.github.io/helm-charts/ diff --git a/clusters/prod/apps/approval/application.yaml b/clusters/prod/apps/approval/application.yaml new file mode 100644 index 0000000..d9a391d --- /dev/null +++ b/clusters/prod/apps/approval/application.yaml @@ -0,0 +1,28 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: approval + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "8" +spec: + project: default + source: + repoURL: https://github.com/PilotDataPlatform/pilot-hdc-platform-gitops.git + targetRevision: main + path: clusters/prod/apps/approval + helm: + valueFiles: + - ../../registry.yaml + - ../../versions.yaml + - values.yaml + destination: + server: https://kubernetes.default.svc + namespace: utility + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/clusters/prod/apps/approval/templates/external-secret.yaml b/clusters/prod/apps/approval/templates/external-secret.yaml new file mode 100644 index 0000000..972dd07 --- /dev/null +++ b/clusters/prod/apps/approval/templates/external-secret.yaml @@ -0,0 +1,25 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: approval-credentials + namespace: utility +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: vault + target: + name: approval-credentials + data: + - secretKey: db-uri + remoteRef: + key: secret/data/approval + property: db-uri + - secretKey: rds-password + remoteRef: + key: secret/data/postgresql + property: approval-user-password + - secretKey: redis-password + remoteRef: + key: secret/data/redis + property: password diff --git a/clusters/prod/apps/approval/values.yaml b/clusters/prod/apps/approval/values.yaml new file mode 100644 index 0000000..15d4031 --- /dev/null +++ b/clusters/prod/apps/approval/values.yaml @@ -0,0 +1,88 @@ +approval-service: + image: + repository: n47w5524.c1.de1.container-registry.ovh.net/hdc-services-image/approval + pullPolicy: IfNotPresent + # tag from versions.yaml via valueFile merge + + fullnameOverride: approval + replicaCount: 1 + + container: + port: 8000 + + service: + type: ClusterIP + port: 8000 + + imagePullSecrets: + - name: docker-registry-secret + + appConfig: + env: prod + config_center_enabled: "false" + config_center_base_url: "http://common.utility:5062/" + + extraEnv: + CORE_ZONE_LABEL: "Core" + GREENROOM_ZONE_LABEL: "Greenroom" + META_SERVICE: "http://metadata.utility:5066" + RDS_SCHEMA_DEFAULT: "pilot_approval" + AUTH_SERVICE: "http://auth.utility:5061" + DATAOPS_SERVICE: "http://dataops.utility:5063" + EMAIL_SERVICE: "http://notification.utility:5065" + METADATA_SERVICE: "http://metadata.utility:5066" + PROJECT_SERVICE: "http://project.utility:5064" + NOTIFICATION_SERVICE: "http://notification.utility:5065" + REDIS_DB: "0" + REDIS_HOST: "redis-master.redis" + REDIS_PORT: "6379" + RDS_DB: "approval" + RDS_HOST: "postgres.utility" + RDS_USER: "approval_user" + RDS_PORT: "5432" + + extraEnvYaml: + - name: DB_URI + valueFrom: + secretKeyRef: + name: approval-credentials + key: db-uri + - name: RDS_PASSWORD + valueFrom: + secretKeyRef: + name: approval-credentials + key: rds-password + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: approval-credentials + key: redis-password + + resources: + requests: + cpu: 10m + memory: 50Mi + limits: + cpu: 500m + memory: 500Mi + + readinessProbe: + tcpSocket: + port: 8000 + initialDelaySeconds: 5 + periodSeconds: 10 + failureThreshold: 3 + + livenessProbe: + httpGet: + path: /v1/health + port: 8000 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 3 + + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 diff --git a/clusters/prod/apps/dataops/Chart.yaml b/clusters/prod/apps/dataops/Chart.yaml new file mode 100644 index 0000000..865bd27 --- /dev/null +++ b/clusters/prod/apps/dataops/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: dataops +version: 0.1.0 +dependencies: + - name: dataops-service + version: "0.2.1" + repository: https://pilotdataplatform.github.io/helm-charts/ diff --git a/clusters/prod/apps/dataops/application.yaml b/clusters/prod/apps/dataops/application.yaml new file mode 100644 index 0000000..236e675 --- /dev/null +++ b/clusters/prod/apps/dataops/application.yaml @@ -0,0 +1,28 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: dataops + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "8" +spec: + project: default + source: + repoURL: https://github.com/PilotDataPlatform/pilot-hdc-platform-gitops.git + targetRevision: main + path: clusters/prod/apps/dataops + helm: + valueFiles: + - ../../registry.yaml + - ../../versions.yaml + - values.yaml + destination: + server: https://kubernetes.default.svc + namespace: utility + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/clusters/prod/apps/dataops/templates/external-secret.yaml b/clusters/prod/apps/dataops/templates/external-secret.yaml new file mode 100644 index 0000000..14c942e --- /dev/null +++ b/clusters/prod/apps/dataops/templates/external-secret.yaml @@ -0,0 +1,21 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: dataops-credentials + namespace: utility +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: vault + target: + name: dataops-credentials + data: + - secretKey: rds-password + remoteRef: + key: secret/data/postgresql + property: dataops-user-password + - secretKey: redis-password + remoteRef: + key: secret/data/redis + property: password diff --git a/clusters/prod/apps/dataops/values.yaml b/clusters/prod/apps/dataops/values.yaml new file mode 100644 index 0000000..f1b93bd --- /dev/null +++ b/clusters/prod/apps/dataops/values.yaml @@ -0,0 +1,84 @@ +dataops-service: + image: + repository: n47w5524.c1.de1.container-registry.ovh.net/hdc-services-image/dataops + pullPolicy: IfNotPresent + # tag comes from versions.yaml via valueFile merge + + fullnameOverride: dataops + replicaCount: 1 + + container: + port: 5063 + + service: + type: ClusterIP + port: 5063 + + imagePullSecrets: + - name: docker-registry-secret + + appConfig: + env: prod + config_center_enabled: "false" + config_center_base_url: "" + + extraEnv: + HOST: "0.0.0.0" + RDS_HOST: "postgres.utility" + RDS_PORT: "5432" + RDS_NAME: "dataops" + RDS_USERNAME: "dataops_user" + RDS_SCHEMA: "public" + RDS_ECHO_SQL_QUERIES: "false" + REDIS_HOST: "redis-master.redis" + REDIS_PORT: "6379" + REDIS_DB: "0" + WORKERS: "2" + LINEAGE_SERVICE: "http://lineage.utility:5064" + QUEUE_SERVICE: "http://queue-producer.greenroom:6060" + METADATA_SERVICE: "http://metadata.utility:5066" + AUTH_SERVICE: "http://auth.utility:5061" + MINIO_HOST: "http://minio.minio" + MINIO_PORT: "9000" + RSA_PUBLIC_KEY: "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFvQkNRQUpRbnpDK1UwN2h3NGliYWNLaWNMbWtTOFR0Mmp6MkFIcVFhWG1sOWUzeCsrZzh5TCtOaDNJSDAzLzdUV2xnbEFSNWRDcEU2cS9LaVpKS1dxU3RCcnlmOHk1UEhaUTJua1YxWXViajRPSThzMklKRFgyNU9XaUNvZFFDRkNwTkNyY1ZmSGpjbnJvRy9icGJoTTBBZ0RFc054UDlpd3NrZ2h3RVFGTDVuc1JWWkpyUEJ0T0dNRWJ4N0xNY3kyeFI2YmlySWFYTmNWSUlyd1g4MVB5bzZENlZRRzlkNkJSUmwvWlVDNGZ5SzZpNGxKUVJvUUV0SW0xNm9PNUFsUGpzYloxd0NXVStlazJJek9aNGRrRnhtUWlwdUpsbjI5TE1jZGdCMlFGZFRNcXBQTXlxWXc3ZmplWmhOL3FoZDFKbUVEeUZOVHVKcEt5Ymd2Y2VnNXdJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0t" + + extraEnvYaml: + - name: RDS_PASSWORD + valueFrom: + secretKeyRef: + name: dataops-credentials + key: rds-password + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: dataops-credentials + key: redis-password + + resources: + requests: + cpu: 10m + memory: 50Mi + limits: + cpu: 500m + memory: 500Mi + + readinessProbe: + tcpSocket: + port: 5063 + initialDelaySeconds: 10 + periodSeconds: 10 + failureThreshold: 3 + + livenessProbe: + httpGet: + path: /v1/health + port: 5063 + periodSeconds: 30 + timeoutSeconds: 5 + failureThreshold: 3 + + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 diff --git a/clusters/prod/apps/dataset/Chart.yaml b/clusters/prod/apps/dataset/Chart.yaml new file mode 100644 index 0000000..c2f28f6 --- /dev/null +++ b/clusters/prod/apps/dataset/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: dataset +version: 0.1.0 +dependencies: + - name: base-chart-hdc + version: "1.0.1" + repository: https://pilotdataplatform.github.io/helm-charts/ diff --git a/clusters/prod/apps/dataset/application.yaml b/clusters/prod/apps/dataset/application.yaml new file mode 100644 index 0000000..3368266 --- /dev/null +++ b/clusters/prod/apps/dataset/application.yaml @@ -0,0 +1,28 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: dataset + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "8" +spec: + project: default + source: + repoURL: https://github.com/PilotDataPlatform/pilot-hdc-platform-gitops.git + targetRevision: main + path: clusters/prod/apps/dataset + helm: + valueFiles: + - ../../registry.yaml + - ../../versions.yaml + - values.yaml + destination: + server: https://kubernetes.default.svc + namespace: utility + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/clusters/prod/apps/dataset/templates/external-secret.yaml b/clusters/prod/apps/dataset/templates/external-secret.yaml new file mode 100644 index 0000000..1fe8b3e --- /dev/null +++ b/clusters/prod/apps/dataset/templates/external-secret.yaml @@ -0,0 +1,36 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: dataset-credentials + namespace: utility +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: vault + target: + name: dataset-credentials + data: + # PostgreSQL credentials + - secretKey: db-username + remoteRef: + key: secret/data/postgresql + property: dataset-user + - secretKey: db-password + remoteRef: + key: secret/data/postgresql + property: dataset-user-password + # MinIO credentials + - secretKey: minio-access-key + remoteRef: + key: secret/data/minio + property: access_key + - secretKey: minio-secret-key + remoteRef: + key: secret/data/minio + property: secret_key + # Redis + - secretKey: redis-password + remoteRef: + key: secret/data/redis + property: password diff --git a/clusters/prod/apps/dataset/values.yaml b/clusters/prod/apps/dataset/values.yaml new file mode 100644 index 0000000..3bed0b8 --- /dev/null +++ b/clusters/prod/apps/dataset/values.yaml @@ -0,0 +1,143 @@ +base-chart-hdc: + image: + repository: n47w5524.c1.de1.container-registry.ovh.net/hdc-services-image/dataset + tag: "2.3.41" + tagPrefix: "dataset" + pullPolicy: IfNotPresent + initContainers: + enabled: true + image: + tagPrefix: alembic + fullnameOverride: dataset + labels: + app: dataset + instance: dataset-service + replicaCount: 1 + container: + name: dataset + ports: + - name: http + containerPort: 5081 + protocol: TCP + service: + type: ClusterIP + ports: + - port: 5081 + targetPort: 5081 + protocol: TCP + name: http + imagePullSecrets: + - name: docker-registry-secret + appConfig: + env: prod + config_center_enabled: false + extraEnv: + # Telemetry + OPEN_TELEMETRY_ENABLED: "true" + ENABLE_PROMETHEUS_METRICS: "false" + # Dataset config + DATASET_FILE_FOLDER: "data" + DATASET_SCHEMA_FOLDER: "schema" + DATASET_CODE_REGEX: "^[a-z0-9]{3,32}$" + ROOT_PATH: "/data/core-storage" + CORE_ZONE_LABEL: "Core" + GREEN_ZONE_LABEL: "Greenroom" + # MinIO + MINIO_OPENID_CLIENT: "react-app" + MINIO_ENDPOINT: "minio.minio:9000" + MINIO_HTTPS: "False" + S3_GATEWAY: "True" + S3_INTERNAL: "minio.minio:9000" + S3_INTERNAL_HTTPS: "False" + S3_HOST: "minio.minio" + S3_PORT: "9000" + S3_HTTPS_ENABLED: "false" + S3_GATEWAY_ENABLED: "true" + S3_BUCKET_ENCRYPTION_ENABLED: "false" + S3_PUBLIC: "object.hdc.ebrains.eu" + S3_PUBLIC_HTTPS: "TRUE" + # Keycloak + KEYCLOAK_URL: "http://keycloak.keycloak/auth/realms/hdc/protocol/openid-connect/token" + # Services + METADATA_SERVICE: "http://metadata.utility:5066" + PROJECT_SERVICE: "http://project.utility:5064" + QUEUE_SERVICE: "http://queue-producer.greenroom:6060" + # Kafka + KAFKA_URL: "kafka-headless.utility:9092" + # Queue + gm_queue_endpoint: "message-bus-greenroom.greenroom" + # Database + RDS_HOST: "postgres.utility" + RDS_PORT: "5432" + RDS_DBNAME: "dataset" + RDS_SCHEMA_DEFAULT: "dataset" + OPSDB_UTILITY_HOST: "postgres.utility" + OPSDB_UTILITY_PORT: "5432" + RUN_MIGRATIONS_ON_BUILD: "false" + ALEMBIC_CONFIG: "migrations/alembic.ini" + # Redis + REDIS_HOST: "redis-master.redis" + REDIS_PORT: "6379" + REDIS_DB: "0" + DATA_OPS_UTIL: "http://dataops.utility:5063" + # Misc + DOWNLOAD_TOKEN_EXPIRE_AT: "5" + MAX_PREVIEW_SIZE: "500000" + ESSENTIALS_NAME: "essential.schema.json" + ESSENTIALS_TPL_NAME: "Essential" + extraEnvYaml: + - name: OPSDB_UTILITY_USERNAME + valueFrom: + secretKeyRef: + name: dataset-credentials + key: db-username + - name: OPSDB_UTILITY_PASSWORD + valueFrom: + secretKeyRef: + name: dataset-credentials + key: db-password + - name: MINIO_USERNAME + valueFrom: + secretKeyRef: + name: dataset-credentials + key: minio-access-key + - name: MINIO_PASSWORD + valueFrom: + secretKeyRef: + name: dataset-credentials + key: minio-secret-key + - name: S3_ACCESS_KEY + valueFrom: + secretKeyRef: + name: dataset-credentials + key: minio-access-key + - name: S3_SECRET_KEY + valueFrom: + secretKeyRef: + name: dataset-credentials + key: minio-secret-key + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: dataset-credentials + key: redis-password + resources: + requests: + cpu: 10m + memory: 50Mi + limits: + cpu: 500m + memory: 500Mi + readinessProbe: + tcpSocket: + port: 5081 + initialDelaySeconds: 5 + periodSeconds: 10 + failureThreshold: 3 + livenessProbe: + httpGet: + path: /v1/health + port: 5081 + periodSeconds: 10 + failureThreshold: 3 + timeoutSeconds: 3 diff --git a/clusters/prod/apps/download-core/Chart.yaml b/clusters/prod/apps/download-core/Chart.yaml new file mode 100644 index 0000000..66a3566 --- /dev/null +++ b/clusters/prod/apps/download-core/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: download-core +version: 0.1.0 +dependencies: + - name: download-service + version: "1.0.6" + repository: https://pilotdataplatform.github.io/helm-charts/ diff --git a/clusters/prod/apps/download-core/application.yaml b/clusters/prod/apps/download-core/application.yaml new file mode 100644 index 0000000..82c5c8b --- /dev/null +++ b/clusters/prod/apps/download-core/application.yaml @@ -0,0 +1,28 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: download-core + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "8" +spec: + project: default + source: + repoURL: https://github.com/PilotDataPlatform/pilot-hdc-platform-gitops.git + targetRevision: main + path: clusters/prod/apps/download-core + helm: + valueFiles: + - ../../registry.yaml + - ../../versions.yaml + - values.yaml + destination: + server: https://kubernetes.default.svc + namespace: core + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/clusters/prod/apps/download-core/templates/external-secret.yaml b/clusters/prod/apps/download-core/templates/external-secret.yaml new file mode 100644 index 0000000..38d9986 --- /dev/null +++ b/clusters/prod/apps/download-core/templates/external-secret.yaml @@ -0,0 +1,29 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: download-credentials + namespace: core +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: vault + target: + name: download-credentials + data: + - secretKey: download-key + remoteRef: + key: secret/data/download + property: download-key + - secretKey: redis-password + remoteRef: + key: secret/data/redis + property: password + - secretKey: minio-access-key + remoteRef: + key: secret/data/minio + property: access_key + - secretKey: minio-secret-key + remoteRef: + key: secret/data/minio + property: secret_key diff --git a/clusters/prod/apps/download-core/values.yaml b/clusters/prod/apps/download-core/values.yaml new file mode 100644 index 0000000..7182a5a --- /dev/null +++ b/clusters/prod/apps/download-core/values.yaml @@ -0,0 +1,94 @@ +download-service: + image: + repository: n47w5524.c1.de1.container-registry.ovh.net/hdc-services-image/download + pullPolicy: IfNotPresent + + + fullnameOverride: download + replicaCount: 1 + + container: + port: 5077 + + service: + type: ClusterIP + port: 5077 + targetPort: 5077 + + imagePullSecrets: + - name: docker-registry-secret + + appConfig: + port: 5077 + env: prod + config_center_enabled: false + ROOT_PATH: "/data/core-storage" + OPEN_TELEMETRY_ENABLED: "False" + + extraEnv: + HOST: "0.0.0.0" + namespace: "core" + S3_INTERNAL: "minio.minio:9000" + S3_INTERNAL_HTTPS: "FALSE" + S3_PUBLIC: "object.hdc.ebrains.eu" + S3_PUBLIC_HTTPS: "TRUE" + + extraEnvYaml: + - name: DOWNLOAD_KEY + valueFrom: + secretKeyRef: + name: download-credentials + key: download-key + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: download-credentials + key: redis-password + - name: S3_ACCESS_KEY + valueFrom: + secretKeyRef: + name: download-credentials + key: minio-access-key + - name: S3_SECRET_KEY + valueFrom: + secretKeyRef: + name: download-credentials + key: minio-secret-key + + extraVolumeMounts: + - name: download-storage + mountPath: /data/core-storage + readOnly: false + + extraVolumes: + - name: download-storage + persistentVolumeClaim: + claimName: core-storage + + resources: + requests: + cpu: 10m + memory: 50Mi + limits: + cpu: 500m + memory: 500Mi + + readinessProbe: + tcpSocket: + port: 5077 + initialDelaySeconds: 5 + periodSeconds: 10 + + livenessProbe: + httpGet: + path: /v1/health + port: 5077 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 3 + + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 diff --git a/clusters/prod/apps/download-greenroom/Chart.yaml b/clusters/prod/apps/download-greenroom/Chart.yaml new file mode 100644 index 0000000..3c017df --- /dev/null +++ b/clusters/prod/apps/download-greenroom/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: download-greenroom +version: 0.1.0 +dependencies: + - name: download-service + version: "1.0.6" + repository: https://pilotdataplatform.github.io/helm-charts/ diff --git a/clusters/prod/apps/download-greenroom/application.yaml b/clusters/prod/apps/download-greenroom/application.yaml new file mode 100644 index 0000000..6a579dc --- /dev/null +++ b/clusters/prod/apps/download-greenroom/application.yaml @@ -0,0 +1,28 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: download-greenroom + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "8" +spec: + project: default + source: + repoURL: https://github.com/PilotDataPlatform/pilot-hdc-platform-gitops.git + targetRevision: main + path: clusters/prod/apps/download-greenroom + helm: + valueFiles: + - ../../registry.yaml + - ../../versions.yaml + - values.yaml + destination: + server: https://kubernetes.default.svc + namespace: greenroom + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/clusters/prod/apps/download-greenroom/templates/external-secret.yaml b/clusters/prod/apps/download-greenroom/templates/external-secret.yaml new file mode 100644 index 0000000..6f2e804 --- /dev/null +++ b/clusters/prod/apps/download-greenroom/templates/external-secret.yaml @@ -0,0 +1,29 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: download-credentials + namespace: greenroom +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: vault + target: + name: download-credentials + data: + - secretKey: download-key + remoteRef: + key: secret/data/download + property: download-key + - secretKey: redis-password + remoteRef: + key: secret/data/redis + property: password + - secretKey: minio-access-key + remoteRef: + key: secret/data/minio + property: access_key + - secretKey: minio-secret-key + remoteRef: + key: secret/data/minio + property: secret_key diff --git a/clusters/prod/apps/download-greenroom/values.yaml b/clusters/prod/apps/download-greenroom/values.yaml new file mode 100644 index 0000000..88b4a7d --- /dev/null +++ b/clusters/prod/apps/download-greenroom/values.yaml @@ -0,0 +1,94 @@ +download-service: + image: + repository: n47w5524.c1.de1.container-registry.ovh.net/hdc-services-image/download + pullPolicy: IfNotPresent + + + fullnameOverride: download + replicaCount: 1 + + container: + port: 5077 + + service: + type: ClusterIP + port: 5077 + targetPort: 5077 + + imagePullSecrets: + - name: docker-registry-secret + + appConfig: + port: 5077 + env: prod + config_center_enabled: false + ROOT_PATH: "/data/greenroom-storage" + OPEN_TELEMETRY_ENABLED: "False" + + extraEnv: + HOST: "0.0.0.0" + namespace: "greenroom" + S3_INTERNAL: "minio.minio:9000" + S3_INTERNAL_HTTPS: "FALSE" + S3_PUBLIC: "object.hdc.ebrains.eu" + S3_PUBLIC_HTTPS: "TRUE" + + extraEnvYaml: + - name: DOWNLOAD_KEY + valueFrom: + secretKeyRef: + name: download-credentials + key: download-key + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: download-credentials + key: redis-password + - name: S3_ACCESS_KEY + valueFrom: + secretKeyRef: + name: download-credentials + key: minio-access-key + - name: S3_SECRET_KEY + valueFrom: + secretKeyRef: + name: download-credentials + key: minio-secret-key + + extraVolumeMounts: + - name: download-storage + mountPath: /data/greenroom-storage + readOnly: false + + extraVolumes: + - name: download-storage + persistentVolumeClaim: + claimName: greenroom-storage + + resources: + requests: + cpu: 10m + memory: 50Mi + limits: + cpu: 500m + memory: 500Mi + + readinessProbe: + tcpSocket: + port: 5077 + initialDelaySeconds: 5 + periodSeconds: 10 + + livenessProbe: + httpGet: + path: /v1/health + port: 5077 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 3 + + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 diff --git a/clusters/prod/apps/kg-integration/Chart.yaml b/clusters/prod/apps/kg-integration/Chart.yaml new file mode 100644 index 0000000..c1ed776 --- /dev/null +++ b/clusters/prod/apps/kg-integration/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: kg-integration +version: 0.1.0 +dependencies: + - name: base-chart + version: "0.1.0" + repository: https://pilotdataplatform.github.io/helm-charts/ diff --git a/clusters/prod/apps/kg-integration/application.yaml b/clusters/prod/apps/kg-integration/application.yaml new file mode 100644 index 0000000..3453fb7 --- /dev/null +++ b/clusters/prod/apps/kg-integration/application.yaml @@ -0,0 +1,27 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: kg-integration + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "9" +spec: + project: default + source: + repoURL: https://github.com/PilotDataPlatform/pilot-hdc-platform-gitops.git + targetRevision: main + path: clusters/prod/apps/kg-integration + helm: + valueFiles: + - ../../registry.yaml + - values.yaml + destination: + server: https://kubernetes.default.svc + namespace: utility + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/clusters/prod/apps/kg-integration/templates/external-secret.yaml b/clusters/prod/apps/kg-integration/templates/external-secret.yaml new file mode 100644 index 0000000..af6edd7 --- /dev/null +++ b/clusters/prod/apps/kg-integration/templates/external-secret.yaml @@ -0,0 +1,21 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: kg-integration-credentials + namespace: utility +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: vault + target: + name: kg-integration-credentials + data: + - secretKey: db-password + remoteRef: + key: secret/data/postgresql + property: kg-integration-user-password + - secretKey: account-secret + remoteRef: + key: secret/data/kg-integration + property: account-secret diff --git a/clusters/prod/apps/kg-integration/values.yaml b/clusters/prod/apps/kg-integration/values.yaml new file mode 100644 index 0000000..d7d5f9b --- /dev/null +++ b/clusters/prod/apps/kg-integration/values.yaml @@ -0,0 +1,73 @@ +base-chart: + image: + repository: n47w5524.c1.de1.container-registry.ovh.net/hdc-services-image/kg-integration + tag: "1.3.34" + pullPolicy: IfNotPresent + fullnameOverride: kg-integration + replicaCount: 1 + container: + port: 8000 + service: + type: ClusterIP + port: 8000 + imagePullSecrets: + - name: docker-registry-secret + appConfig: + env: prod + srv_namespace: service_kg + config_center_enabled: "false" + config_center_base_url: "" + port: "8000" + extraEnv: + HOST: "0.0.0.0" + PORT: "8000" + SRV_NAMESPACE: "utility" + # Database + RDS_SCHEMA_DEFAULT: "kg_integration" + RDS_DB: "kg_integration" + RDS_HOST: "postgres.utility" + RDS_USER: "kg_integration_user" + RDS_PORT: "5432" + KG_ENV: "prod" + KG_PREFIX: "collab-" + KG_SERVICE_ACCOUNT_ID: "hdcclient-kg" + # Collaboratory + COLLAB_ENV: "prod" + COLLAB_PREFIX: "hdc-" + # Keycloak + KEYCLOAK_URL: "http://keycloak.keycloak/auth/" + KEYCLOAK_REALM: "hdc" + KEYCLOAK_BROKER: "ebrains-keycloak" + # Deployed services + AUTH_SERVICE: "http://auth.utility:5061" + DATASET_SERVICE: "http://dataset.utility:5081" + PROJECT_SERVICE: "http://project.utility:5064" + # Kafka + KAFKA_URL: "kafka-headless.utility:9092" + # Telemetry + OPEN_TELEMETRY_ENABLED: "false" + extraEnvYaml: + - name: RDS_PASSWORD + valueFrom: + secretKeyRef: + name: kg-integration-credentials + key: db-password + - name: KG_SERVICE_ACCOUNT_SECRET + valueFrom: + secretKeyRef: + name: kg-integration-credentials + key: account-secret + resources: + requests: + cpu: 10m + memory: 50Mi + limits: + cpu: 500m + memory: 500Mi + readinessProbe: + failureThreshold: 3 + initialDelaySeconds: 5 + periodSeconds: 10 + successThreshold: 1 + tcpSocket: + port: 8000 diff --git a/clusters/prod/apps/kong-postgresql/Chart.yaml b/clusters/prod/apps/kong-postgresql/Chart.yaml new file mode 100644 index 0000000..6336bd8 --- /dev/null +++ b/clusters/prod/apps/kong-postgresql/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: kong-postgresql +version: 0.1.0 +dependencies: + - name: postgresql + version: "15.5.17" + repository: https://pilotdataplatform.github.io/helm-charts/ diff --git a/clusters/prod/apps/kong-postgresql/application.yaml b/clusters/prod/apps/kong-postgresql/application.yaml new file mode 100644 index 0000000..59bbe24 --- /dev/null +++ b/clusters/prod/apps/kong-postgresql/application.yaml @@ -0,0 +1,28 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: kong-postgresql + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "8" + # NO finalizer — StatefulSet with PVC +spec: + project: default + source: + repoURL: https://github.com/PilotDataPlatform/pilot-hdc-platform-gitops.git + targetRevision: main + path: clusters/prod/apps/kong-postgresql + helm: + valueFiles: + - ../../registry.yaml + - values.yaml + destination: + server: https://kubernetes.default.svc + namespace: utility + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/clusters/prod/apps/kong-postgresql/templates/external-secret.yaml b/clusters/prod/apps/kong-postgresql/templates/external-secret.yaml new file mode 100644 index 0000000..413ccf4 --- /dev/null +++ b/clusters/prod/apps/kong-postgresql/templates/external-secret.yaml @@ -0,0 +1,21 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: kong-postgresql-credentials + namespace: utility +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: vault + target: + name: kong-postgresql-credentials + data: + - secretKey: password + remoteRef: + key: secret/data/kong + property: postgresql-password + - secretKey: postgres-password + remoteRef: + key: secret/data/kong + property: postgres-password diff --git a/clusters/prod/apps/kong-postgresql/values.yaml b/clusters/prod/apps/kong-postgresql/values.yaml new file mode 100644 index 0000000..79c1854 --- /dev/null +++ b/clusters/prod/apps/kong-postgresql/values.yaml @@ -0,0 +1,40 @@ +postgresql: + fullnameOverride: kong-postgresql + + global: + imagePullSecrets: + - docker-registry-secret + postgresql: + auth: + database: kong + username: kong + existingSecret: kong-postgresql-credentials + secretKeys: + adminPasswordKey: postgres-password + userPasswordKey: password + + image: + repository: hdc-services-external/bitnami/postgresql + + architecture: standalone + + primary: + pgHbaConfiguration: |- + local all all trust + host all all 127.0.0.1/32 trust + host all all ::1/128 trust + host all all 0.0.0.0/0 md5 + host all all ::/0 md5 + extendedConfiguration: |- + password_encryption = md5 + persistence: + size: 5Gi + storageClass: "csi-cinder-high-speed" + + resources: + limits: + cpu: 500m + memory: 256Mi + requests: + cpu: 10m + memory: 64Mi diff --git a/clusters/prod/apps/kong/Chart.yaml b/clusters/prod/apps/kong/Chart.yaml new file mode 100644 index 0000000..0b6f2bc --- /dev/null +++ b/clusters/prod/apps/kong/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: kong +version: 0.1.0 +dependencies: + - name: kong + version: "9.1.8" + repository: https://pilotdataplatform.github.io/helm-charts/ diff --git a/clusters/prod/apps/kong/application.yaml b/clusters/prod/apps/kong/application.yaml new file mode 100644 index 0000000..ed853ea --- /dev/null +++ b/clusters/prod/apps/kong/application.yaml @@ -0,0 +1,28 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: kong + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "9" +spec: + project: default + source: + repoURL: https://github.com/PilotDataPlatform/pilot-hdc-platform-gitops.git + targetRevision: main + path: clusters/prod/apps/kong + helm: + valueFiles: + - ../../registry.yaml + - ../../versions.yaml + - values.yaml + destination: + server: https://kubernetes.default.svc + namespace: utility + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/clusters/prod/apps/kong/values.yaml b/clusters/prod/apps/kong/values.yaml new file mode 100644 index 0000000..de87baf --- /dev/null +++ b/clusters/prod/apps/kong/values.yaml @@ -0,0 +1,76 @@ +kong: + migration: + annotations: + helm.sh/hook: post-install, pre-upgrade + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded + # Chart bug: postgresql.external.database not mapped to KONG_PG_DATABASE + extraEnvVars: + - name: KONG_PG_DATABASE + value: "kong" + + global: + imagePullSecrets: + - docker-registry-secret + + image: + repository: hdc-services-external/kong-with-oidc + pullPolicy: IfNotPresent + debug: false + + database: postgresql + replicaCount: 1 + + # Disable bundled PostgreSQL — using external kong-postgresql app (wave 8) + postgresql: + enabled: false + external: + host: kong-postgresql + port: 5432 + user: kong + database: kong + existingSecret: kong-postgresql-credentials + existingSecretPasswordKey: password + + ingress: + enabled: true + hostname: api.hdc.ebrains.eu + path: / + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + kubernetes.io/ingress.class: nginx + nginx.ingress.kubernetes.io/affinity: cookie + nginx.ingress.kubernetes.io/proxy-body-size: 20m + nginx.ingress.kubernetes.io/proxy-buffer-size: 512k + nginx.ingress.kubernetes.io/proxy-buffering: "on" + nginx.ingress.kubernetes.io/proxy-buffers-number: "8" + nginx.ingress.kubernetes.io/proxy-connect-timeout: 180s + nginx.ingress.kubernetes.io/proxy-max-temp-file-size: "0" + nginx.ingress.kubernetes.io/proxy-read-timeout: 180s + nginx.ingress.kubernetes.io/proxy-send-timeout: 180s + tls: true + + ingressController: + enabled: false + + kong: + extraEnvVars: + - name: KONG_LOG_LEVEL + value: "info" + - name: KONG_PLUGINS + value: "bundled,oidc" + - name: KONG_LUA_SSL_TRUSTED_CERTIFICATE + value: "/etc/ssl/certs/ca-certificates.crt" + # Chart bug: postgresql.external.database not mapped to KONG_PG_DATABASE + - name: KONG_PG_DATABASE + value: "kong" + + service: + exposeAdmin: true + + resources: + requests: + memory: "256Mi" + cpu: "100m" + limits: + memory: "512Mi" + cpu: "500m" diff --git a/clusters/prod/apps/metadata-event-handler/Chart.yaml b/clusters/prod/apps/metadata-event-handler/Chart.yaml new file mode 100644 index 0000000..99519fb --- /dev/null +++ b/clusters/prod/apps/metadata-event-handler/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: metadata-event-handler +version: 0.1.0 +dependencies: + - name: metadata-event-handler + version: "0.1.1" + repository: https://pilotdataplatform.github.io/helm-charts/ diff --git a/clusters/prod/apps/metadata-event-handler/application.yaml b/clusters/prod/apps/metadata-event-handler/application.yaml new file mode 100644 index 0000000..a48961c --- /dev/null +++ b/clusters/prod/apps/metadata-event-handler/application.yaml @@ -0,0 +1,28 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: metadata-event-handler + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "9" +spec: + project: default + source: + repoURL: https://github.com/PilotDataPlatform/pilot-hdc-platform-gitops.git + targetRevision: main + path: clusters/prod/apps/metadata-event-handler + helm: + valueFiles: + - ../../registry.yaml + - ../../versions.yaml + - values.yaml + destination: + server: https://kubernetes.default.svc + namespace: utility + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/clusters/prod/apps/metadata-event-handler/values.yaml b/clusters/prod/apps/metadata-event-handler/values.yaml new file mode 100644 index 0000000..e7d9904 --- /dev/null +++ b/clusters/prod/apps/metadata-event-handler/values.yaml @@ -0,0 +1,57 @@ +metadata-event-handler: + image: + repository: n47w5524.c1.de1.container-registry.ovh.net/hdc-services-image/metadata_event_handler + pullPolicy: IfNotPresent + + fullnameOverride: metadata-event-handler + replicaCount: 1 + + container: + port: 5069 + + service: + type: ClusterIP + port: 5069 + targetPort: 5069 + + imagePullSecrets: + - name: docker-registry-secret + + # appConfig: only KAFKA_SERVICE and ELASTICSEARCH_SERVICE are rendered by the chart template + appConfig: + KAFKA_SERVICE: "kafka.utility:9092" + ELASTICSEARCH_SERVICE: "http://elasticsearch-master.utility:9200" + + extraEnv: + KAFKA_TOPICS: '["metadata.items", "metadata.items.activity", "dataset.activity"]' + METADATA_SERVICE: "http://metadata.utility:5066" + PROJECT_SERVICE: "http://project.utility:5064" + APP_NAME: "metadata-event-handler" + VERSION: "1.0.0" + HOST: "0.0.0.0" + PORT: "5069" + WORKERS: "1" + + resources: + limits: + cpu: "1" + memory: 100Mi + requests: + cpu: 10m + memory: 10Mi + + livenessProbe: + failureThreshold: 3 + httpGet: + path: /v1/health/ + port: 5069 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 3 + + updateStrategy: + rollingUpdate: + maxSurge: 1 + maxUnavailable: 33% + type: RollingUpdate diff --git a/clusters/prod/apps/metadata/Chart.yaml b/clusters/prod/apps/metadata/Chart.yaml new file mode 100644 index 0000000..dc72443 --- /dev/null +++ b/clusters/prod/apps/metadata/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: metadata +version: 0.1.0 +dependencies: + - name: metadata-service + version: "1.0.0" + repository: https://pilotdataplatform.github.io/helm-charts/ diff --git a/clusters/prod/apps/metadata/application.yaml b/clusters/prod/apps/metadata/application.yaml new file mode 100644 index 0000000..d1a7ac0 --- /dev/null +++ b/clusters/prod/apps/metadata/application.yaml @@ -0,0 +1,28 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: metadata + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "8" +spec: + project: default + source: + repoURL: https://github.com/PilotDataPlatform/pilot-hdc-platform-gitops.git + targetRevision: main + path: clusters/prod/apps/metadata + helm: + valueFiles: + - ../../registry.yaml + - ../../versions.yaml + - values.yaml + destination: + server: https://kubernetes.default.svc + namespace: utility + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/clusters/prod/apps/metadata/templates/external-secret.yaml b/clusters/prod/apps/metadata/templates/external-secret.yaml new file mode 100644 index 0000000..46b7832 --- /dev/null +++ b/clusters/prod/apps/metadata/templates/external-secret.yaml @@ -0,0 +1,17 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: metadata-credentials + namespace: utility +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: vault + target: + name: metadata-credentials + data: + - secretKey: metadata-user-password + remoteRef: + key: secret/data/postgresql + property: metadata-user-password diff --git a/clusters/prod/apps/metadata/values.yaml b/clusters/prod/apps/metadata/values.yaml new file mode 100644 index 0000000..9c980fb --- /dev/null +++ b/clusters/prod/apps/metadata/values.yaml @@ -0,0 +1,75 @@ +metadata-service: + image: + repository: n47w5524.c1.de1.container-registry.ovh.net/hdc-services-image/metadata + pullPolicy: IfNotPresent + + fullnameOverride: metadata + replicaCount: 1 + + container: + port: 5066 + + service: + type: ClusterIP + port: 5066 + targetPort: 5066 + + imagePullSecrets: + - name: docker-registry-secret + + appConfig: + port: 5066 + env: prod + srv_namespace: metadata + config_center_enabled: false + METADATA_SCHEMA: metadata + OPSDB_UTILITY_HOST: postgres.utility + OPSDB_UTILITY_PORT: "5432" + OPSDB_UTILITY_USERNAME: metadata_user + DCM_PROJECT_ID: generate_id + RUN_MIGRATIONS: "true" + AUTH_HOST: "http://auth.utility:5061" + KAFKA_URL: "kafka.utility:9092" + KAFKA_TOPIC: "metadata.items" + + extraEnv: + RSA_PUBLIC_KEY: "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFvQkNRQUpRbnpDK1UwN2h3NGliYWNLaWNMbWtTOFR0Mmp6MkFIcVFhWG1sOWUzeCsrZzh5TCtOaDNJSDAzLzdUV2xnbEFSNWRDcEU2cS9LaVpKS1dxU3RCcnlmOHk1UEhaUTJua1YxWXViajRPSThzMklKRFgyNU9XaUNvZFFDRkNwTkNyY1ZmSGpjbnJvRy9icGJoTTBBZ0RFc054UDlpd3NrZ2h3RVFGTDVuc1JWWkpyUEJ0T0dNRWJ4N0xNY3kyeFI2YmlySWFYTmNWSUlyd1g4MVB5bzZENlZRRzlkNkJSUmwvWlVDNGZ5SzZpNGxKUVJvUUV0SW0xNm9PNUFsUGpzYloxd0NXVStlazJJek9aNGRrRnhtUWlwdUpsbjI5TE1jZGdCMlFGZFRNcXBQTXlxWXc3ZmplWmhOL3FoZDFKbUVEeUZOVHVKcEt5Ymd2Y2VnNXdJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0t" + + extraEnvYaml: + - name: OPSDB_UTILITY_PASSWORD + valueFrom: + secretKeyRef: + name: metadata-credentials + key: metadata-user-password + + resources: + limits: + cpu: "1" + memory: 500Mi + requests: + cpu: 10m + memory: 50Mi + + readinessProbe: + failureThreshold: 3 + initialDelaySeconds: 5 + periodSeconds: 10 + successThreshold: 1 + tcpSocket: + port: 5066 + + livenessProbe: + failureThreshold: 3 + httpGet: + path: /v1/health + port: 5066 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 3 + + updateStrategy: + rollingUpdate: + maxSurge: 1 + maxUnavailable: 33% + type: RollingUpdate diff --git a/clusters/prod/apps/notification/Chart.yaml b/clusters/prod/apps/notification/Chart.yaml new file mode 100644 index 0000000..8dd8fef --- /dev/null +++ b/clusters/prod/apps/notification/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: notification +version: 0.1.0 +dependencies: + - name: notification-service + version: "0.3.2" + repository: https://pilotdataplatform.github.io/helm-charts/ diff --git a/clusters/prod/apps/notification/application.yaml b/clusters/prod/apps/notification/application.yaml new file mode 100644 index 0000000..447ba05 --- /dev/null +++ b/clusters/prod/apps/notification/application.yaml @@ -0,0 +1,28 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: notification + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "8" +spec: + project: default + source: + repoURL: https://github.com/PilotDataPlatform/pilot-hdc-platform-gitops.git + targetRevision: main + path: clusters/prod/apps/notification + helm: + valueFiles: + - ../../registry.yaml + - ../../versions.yaml + - values.yaml + destination: + server: https://kubernetes.default.svc + namespace: utility + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/clusters/prod/apps/notification/templates/external-secret.yaml b/clusters/prod/apps/notification/templates/external-secret.yaml new file mode 100644 index 0000000..c960f52 --- /dev/null +++ b/clusters/prod/apps/notification/templates/external-secret.yaml @@ -0,0 +1,17 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: notification-credentials + namespace: utility +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: vault + target: + name: notification-credentials + data: + - secretKey: notification-user-password + remoteRef: + key: secret/data/postgresql + property: notification-user-password diff --git a/clusters/prod/apps/notification/values.yaml b/clusters/prod/apps/notification/values.yaml new file mode 100644 index 0000000..d938aac --- /dev/null +++ b/clusters/prod/apps/notification/values.yaml @@ -0,0 +1,78 @@ +notification-service: + image: + repository: n47w5524.c1.de1.container-registry.ovh.net/hdc-services-image/notification + pullPolicy: IfNotPresent + # tag 2.2.10 from versions.yaml + # chart prefixes: notification-{tag} (main), alembic-{tag} (init) + + fullnameOverride: notification + replicaCount: 1 + + container: + port: 5065 + + service: + type: ClusterIP + port: 5065 + targetPort: 5065 + + imagePullSecrets: + - name: docker-registry-secret + + # appConfig: only the 5 vars the chart template renders explicitly + appConfig: + port: 5065 + env: prod + config_center_enabled: "false" + config_center_base_url: "http://common.utility:5062/" + srv_namespace: service_notification + + # All other config via extraEnv (applied to BOTH init + main containers) + extraEnv: + # App + HOST: "0.0.0.0" + WORKERS: "2" + PLATFORM_NAME: PILOT + namespace: utility + OPEN_TELEMETRY_ENABLED: "false" + POSTFIX: TODO-PROD-SMTP-RELAY # TODO: replace with prod SMTP relay + SMTP_PORT: "25" + SMTP_USER: "" + SMTP_PASS: "" + EMAIL_ATTACHMENT_MAX_SIZE_BYTES: "2097152" + # Database + RDS_HOST: postgres.utility + RDS_PORT: "5432" + RDS_USER: notification_user + RDS_DB_NAME: notifications + + # Secret env vars (applied to BOTH init + main containers) + extraEnvYaml: + - name: RDS_PWD + valueFrom: + secretKeyRef: + name: notification-credentials + key: notification-user-password + + resources: + requests: + cpu: 10m + memory: 50Mi + limits: + cpu: 500m + memory: 500Mi + + readinessProbe: + tcpSocket: + port: 5065 + initialDelaySeconds: 5 + periodSeconds: 10 + failureThreshold: 3 + + livenessProbe: + httpGet: + path: /v1/health + port: 5065 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 3 diff --git a/clusters/prod/apps/pipelinewatch/Chart.yaml b/clusters/prod/apps/pipelinewatch/Chart.yaml new file mode 100644 index 0000000..f18fb1a --- /dev/null +++ b/clusters/prod/apps/pipelinewatch/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: pipelinewatch +version: 0.1.0 +dependencies: + - name: pipelinewatch-service + version: "0.4.2" + repository: https://pilotdataplatform.github.io/helm-charts/ diff --git a/clusters/prod/apps/pipelinewatch/application.yaml b/clusters/prod/apps/pipelinewatch/application.yaml new file mode 100644 index 0000000..3e3b943 --- /dev/null +++ b/clusters/prod/apps/pipelinewatch/application.yaml @@ -0,0 +1,28 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: pipelinewatch + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "8" +spec: + project: default + source: + repoURL: https://github.com/PilotDataPlatform/pilot-hdc-platform-gitops.git + targetRevision: main + path: clusters/prod/apps/pipelinewatch + helm: + valueFiles: + - ../../registry.yaml + - ../../versions.yaml + - values.yaml + destination: + server: https://kubernetes.default.svc + namespace: greenroom + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/clusters/prod/apps/pipelinewatch/templates/rbac.yaml b/clusters/prod/apps/pipelinewatch/templates/rbac.yaml new file mode 100644 index 0000000..61cd432 --- /dev/null +++ b/clusters/prod/apps/pipelinewatch/templates/rbac.yaml @@ -0,0 +1,21 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: pipelinewatch-job-watcher +rules: + - apiGroups: ["batch"] + resources: ["jobs"] + verbs: ["get", "list", "watch", "delete"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: pipelinewatch-job-watcher +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: pipelinewatch-job-watcher +subjects: + - kind: ServiceAccount + name: pipelinewatch + namespace: greenroom diff --git a/clusters/prod/apps/pipelinewatch/values.yaml b/clusters/prod/apps/pipelinewatch/values.yaml new file mode 100644 index 0000000..9606c3c --- /dev/null +++ b/clusters/prod/apps/pipelinewatch/values.yaml @@ -0,0 +1,46 @@ +pipelinewatch-service: + image: + repository: n47w5524.c1.de1.container-registry.ovh.net/hdc-services-image/pipelinewatch + pullPolicy: IfNotPresent + + fullnameOverride: pipelinewatch-k8s-job + replicaCount: 1 + + container: + port: 6063 + command: /app/worker_k8s_job_watch.py + + service: + type: ClusterIP + port: 6063 + targetPort: 6063 + + imagePullSecrets: + - name: docker-registry-secret + + serviceAccount: + create: true + name: pipelinewatch + + appConfig: + env: prod + config_center_enabled: "false" + + extraEnv: + GR_ZONE_LABEL: "Greenroom" + CORE_ZONE_LABEL: "Core" + METADATA_SERVICE: "http://metadata.utility:5066" + DATAOPS_SERVICE: "http://dataops.utility:5063" + + volumes: + name: nfsvol + mountPath: /data/core-storage + claimName: greenroom-storage + + resources: + requests: + cpu: 10m + memory: 50Mi + limits: + cpu: 1 + memory: 1000Mi diff --git a/clusters/prod/apps/project/Chart.yaml b/clusters/prod/apps/project/Chart.yaml new file mode 100644 index 0000000..d3fff92 --- /dev/null +++ b/clusters/prod/apps/project/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: project +version: 0.1.0 +dependencies: + - name: project-service + version: "0.2.1" + repository: https://pilotdataplatform.github.io/helm-charts/ diff --git a/clusters/prod/apps/project/application.yaml b/clusters/prod/apps/project/application.yaml new file mode 100644 index 0000000..b622b7a --- /dev/null +++ b/clusters/prod/apps/project/application.yaml @@ -0,0 +1,28 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: project + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "8" +spec: + project: default + source: + repoURL: https://github.com/PilotDataPlatform/pilot-hdc-platform-gitops.git + targetRevision: main + path: clusters/prod/apps/project + helm: + valueFiles: + - ../../registry.yaml + - ../../versions.yaml + - values.yaml + destination: + server: https://kubernetes.default.svc + namespace: utility + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/clusters/prod/apps/project/templates/external-secret.yaml b/clusters/prod/apps/project/templates/external-secret.yaml new file mode 100644 index 0000000..faf5e08 --- /dev/null +++ b/clusters/prod/apps/project/templates/external-secret.yaml @@ -0,0 +1,25 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: project-credentials + namespace: utility +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: vault + target: + name: project-credentials + data: + - secretKey: project-user-password + remoteRef: + key: secret/data/postgresql + property: project-user-password + - secretKey: minio-access-key + remoteRef: + key: secret/data/minio + property: access_key + - secretKey: minio-secret-key + remoteRef: + key: secret/data/minio + property: secret_key diff --git a/clusters/prod/apps/project/values.yaml b/clusters/prod/apps/project/values.yaml new file mode 100644 index 0000000..31e42b4 --- /dev/null +++ b/clusters/prod/apps/project/values.yaml @@ -0,0 +1,102 @@ +project-service: + image: + repository: n47w5524.c1.de1.container-registry.ovh.net/hdc-services-image/project + pullPolicy: IfNotPresent + + fullnameOverride: project + replicaCount: 1 + + container: + port: 5064 + + service: + type: ClusterIP + port: 5064 + + imagePullSecrets: + - name: docker-registry-secret + + appConfig: + port: 5064 + env: prod + srv_namespace: service_project + config_center_enabled: false + config_center_base_url: http://common.utility:5062/ + + extraEnv: + APP_NAME: "project" + HOST: "0.0.0.0" + PORT: "5064" + WORKERS: "1" + RDS_DB_HOST: postgres.utility + RDS_DB_PORT: "5432" + RDS_DB_USERNAME: project_user + RDS_DB_NAME: project + RDS_ECHO_SQL_QUERIES: "false" + DCM_PROJECT_ID: generate_id + RUN_MIGRATIONS: "true" + AUTH_SERVICE: "http://auth.utility:5061" + METADATA_SERVICE: "http://metadata.utility:5066" + S3_HOST: minio.minio + S3_PORT: "9000" + S3_HTTPS_ENABLED: "false" + S3_GATEWAY_ENABLED: "false" + S3_BUCKET_ENCRYPTION_ENABLED: "false" + S3_BUCKET_FOR_PROJECT_LOGOS: project-logos + S3_PREFIX_FOR_PROJECT_IMAGE_URLS: "https://object.hdc.ebrains.eu/project-logos" + OBJECT_STORAGE_PROVIDER: S3 + OPEN_TELEMETRY_ENABLED: "false" + LOG_LEVEL_DEFAULT: "30" + LOG_LEVEL_STDOUT: "30" + LOG_LEVEL_STDERR: "40" + SERVICE_CLIENT_TIMEOUT: "5" + ICON_SIZE_LIMIT: "16777216" + + extraEnvYaml: + - name: RDS_DB_PASSWORD + valueFrom: + secretKeyRef: + name: project-credentials + key: project-user-password + - name: S3_ACCESS_KEY + valueFrom: + secretKeyRef: + name: project-credentials + key: minio-access-key + - name: S3_SECRET_KEY + valueFrom: + secretKeyRef: + name: project-credentials + key: minio-secret-key + + resources: + limits: + cpu: "1" + memory: 500Mi + requests: + cpu: 10m + memory: 50Mi + + readinessProbe: + failureThreshold: 3 + initialDelaySeconds: 5 + periodSeconds: 10 + successThreshold: 1 + tcpSocket: + port: 5064 + + livenessProbe: + failureThreshold: 3 + httpGet: + path: /v1/health/ + port: 5064 + scheme: HTTP + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 3 + + updateStrategy: + rollingUpdate: + maxSurge: 1 + maxUnavailable: 33% + type: RollingUpdate diff --git a/clusters/prod/apps/queue-consumer/Chart.yaml b/clusters/prod/apps/queue-consumer/Chart.yaml new file mode 100644 index 0000000..bc88288 --- /dev/null +++ b/clusters/prod/apps/queue-consumer/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: queue-consumer +version: 0.1.0 +dependencies: + - name: queue-service + version: "0.3.0" # synced from versions.yaml via make sync-versions + repository: https://pilotdataplatform.github.io/helm-charts/ diff --git a/clusters/prod/apps/queue-consumer/application.yaml b/clusters/prod/apps/queue-consumer/application.yaml new file mode 100644 index 0000000..93dbb4c --- /dev/null +++ b/clusters/prod/apps/queue-consumer/application.yaml @@ -0,0 +1,28 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: queue-consumer + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "8" +spec: + project: default + source: + repoURL: https://github.com/PilotDataPlatform/pilot-hdc-platform-gitops.git + targetRevision: main + path: clusters/prod/apps/queue-consumer + helm: + valueFiles: + - ../../registry.yaml + # no versions.yaml — image tag is in values.yaml (prefixed per sub-service) + - values.yaml + destination: + server: https://kubernetes.default.svc + namespace: greenroom + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/clusters/prod/apps/queue-consumer/templates/external-secret.yaml b/clusters/prod/apps/queue-consumer/templates/external-secret.yaml new file mode 100644 index 0000000..4a26c84 --- /dev/null +++ b/clusters/prod/apps/queue-consumer/templates/external-secret.yaml @@ -0,0 +1,33 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: queue-consumer-credentials + namespace: greenroom +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: vault + target: + name: queue-consumer-credentials + data: + - secretKey: rabbitmq-username + remoteRef: + key: secret/data/rabbitmq + property: username + - secretKey: rabbitmq-password + remoteRef: + key: secret/data/rabbitmq + property: password + - secretKey: redis-password + remoteRef: + key: secret/data/redis + property: password + - secretKey: minio-access-key + remoteRef: + key: secret/data/minio + property: access_key + - secretKey: minio-secret-key + remoteRef: + key: secret/data/minio + property: secret_key diff --git a/clusters/prod/apps/queue-consumer/templates/rbac.yaml b/clusters/prod/apps/queue-consumer/templates/rbac.yaml new file mode 100644 index 0000000..365f768 --- /dev/null +++ b/clusters/prod/apps/queue-consumer/templates/rbac.yaml @@ -0,0 +1,23 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: queue-consumer-job-creator + namespace: greenroom +rules: + - apiGroups: ["batch"] + resources: ["jobs"] + verbs: ["create"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: queue-consumer-job-creator + namespace: greenroom +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: queue-consumer-job-creator +subjects: + - kind: ServiceAccount + name: queue-consumer + namespace: greenroom diff --git a/clusters/prod/apps/queue-consumer/values.yaml b/clusters/prod/apps/queue-consumer/values.yaml new file mode 100644 index 0000000..147098d --- /dev/null +++ b/clusters/prod/apps/queue-consumer/values.yaml @@ -0,0 +1,104 @@ +queue-service: + image: + repository: n47w5524.c1.de1.container-registry.ovh.net/hdc-services-image/queue/consumer + pullPolicy: IfNotPresent + tag: "consumer-2.2.16" # prefixed tag — NOT from versions.yaml + fullnameOverride: queue-consumer + replicaCount: 1 + container: + port: 6060 + service: + type: ClusterIP + port: 6060 + targetPort: 6060 + imagePullSecrets: + - name: docker-registry-secret + serviceAccount: + create: true + name: queue-consumer + appConfig: + env: prod + config_center_enabled: "false" + config_center_base_url: "" + extraEnv: + # -- Pipeline config -- + bids_validate_pipeline: "bids_validate" + copy_pipeline: "data_transfer" + copy_pipeline_folder: "data_transfer_folder" + move_pipeline: "data_delete" + move_pipeline_folder: "data_delete_folder" + # -- Pipeline images (OVH registry) -- + bids_validate_image: "n47w5524.c1.de1.container-registry.ovh.net/hdc-services-image/pipelines/bids-validator:bids-validator-2.2.15" + data_transfer_image: "n47w5524.c1.de1.container-registry.ovh.net/hdc-services-image/pipelines/filecopy:filecopy-2.2.16" + # -- Storage (pass-through to spawned Jobs — consumer doesn't touch FS itself) -- + data_lake: "/data/core-storage" + claim_name: "greenroom-storage" + NFS_MOUNT: "nfsvol" + # -- Zone labels -- + GREEN_ZONE_LABEL: "Greenroom" + CORE_ZONE_LABEL: "Core" + # -- Database (pass-through to spawned Jobs — consumer has NO DB driver) -- + RDS_DBNAME: "approval" + RDS_HOST: "postgres.utility" + RDS_PORT: "5432" + # -- S3 / MinIO -- + S3_HOST: "minio.minio" + S3_PORT: "9000" + S3_INTERNAL_HTTPS: "false" + # -- RabbitMQ -- + gm_queue_endpoint: "message-bus-greenroom.greenroom" + # -- Queue config -- + gr_queue: "gr_queue" + gr_exchange: "gr_exchange" + # -- Inter-service URLs -- + DATAOPS_SERVICE: "http://dataops.utility:5063" + DATASET_SERVICE: "http://dataset.utility:5081" + QUEUE_SERVICE: "http://queue-producer.greenroom:6060" + METADATA_SERVICE: "http://metadata.utility:5066" + PROJECT_SERVICE: "http://project.utility:5064" + APPROVAL_SERVICE: "http://approval.utility:8000" + NOTIFICATION_SERVICE: "http://notification.utility:5065" + KAFKA_URL: "kafka.utility:9092" + REDIS_HOST: "redis-master.redis" + REDIS_PORT: "6379" + ATLAS_HOST: "atlas.utility" + ATLAS_PORT: "21000" + # Secrets — all pass-through to spawned Jobs (consumer itself uses none except RabbitMQ creds) + extraEnvYaml: + - name: gm_username + valueFrom: + secretKeyRef: + name: queue-consumer-credentials + key: rabbitmq-username + - name: gm_password + valueFrom: + secretKeyRef: + name: queue-consumer-credentials + key: rabbitmq-password + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: queue-consumer-credentials + key: redis-password + - name: S3_ACCESS_KEY + valueFrom: + secretKeyRef: + name: queue-consumer-credentials + key: minio-access-key + - name: S3_SECRET_KEY + valueFrom: + secretKeyRef: + name: queue-consumer-credentials + key: minio-secret-key + resources: + requests: + cpu: 10m + memory: 50Mi + limits: + cpu: 1 + memory: 1000Mi + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: "33%" diff --git a/clusters/prod/apps/queue-producer/Chart.yaml b/clusters/prod/apps/queue-producer/Chart.yaml new file mode 100644 index 0000000..fd222d1 --- /dev/null +++ b/clusters/prod/apps/queue-producer/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: queue-producer +version: 0.1.0 +dependencies: + - name: queue-service + version: "0.3.0" # synced from versions.yaml via make sync-versions + repository: https://pilotdataplatform.github.io/helm-charts/ diff --git a/clusters/prod/apps/queue-producer/application.yaml b/clusters/prod/apps/queue-producer/application.yaml new file mode 100644 index 0000000..96ce9dd --- /dev/null +++ b/clusters/prod/apps/queue-producer/application.yaml @@ -0,0 +1,27 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: queue-producer + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "8" +spec: + project: default + source: + repoURL: https://github.com/PilotDataPlatform/pilot-hdc-platform-gitops.git + targetRevision: main + path: clusters/prod/apps/queue-producer + helm: + valueFiles: + - ../../registry.yaml + - values.yaml + destination: + server: https://kubernetes.default.svc + namespace: greenroom + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/clusters/prod/apps/queue-producer/templates/external-secret.yaml b/clusters/prod/apps/queue-producer/templates/external-secret.yaml new file mode 100644 index 0000000..cb6cb15 --- /dev/null +++ b/clusters/prod/apps/queue-producer/templates/external-secret.yaml @@ -0,0 +1,21 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: queue-producer-credentials + namespace: greenroom +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: vault + target: + name: queue-producer-credentials + data: + - secretKey: rabbitmq-username + remoteRef: + key: secret/data/rabbitmq + property: username + - secretKey: rabbitmq-password + remoteRef: + key: secret/data/rabbitmq + property: password diff --git a/clusters/prod/apps/queue-producer/values.yaml b/clusters/prod/apps/queue-producer/values.yaml new file mode 100644 index 0000000..8d134e3 --- /dev/null +++ b/clusters/prod/apps/queue-producer/values.yaml @@ -0,0 +1,57 @@ +queue-service: + image: + repository: n47w5524.c1.de1.container-registry.ovh.net/hdc-services-image/queue/producer + pullPolicy: IfNotPresent + tag: "producer-2.2.16" + fullnameOverride: queue-producer + replicaCount: 1 + container: + port: 6060 + service: + type: ClusterIP + port: 6060 + targetPort: 6060 + imagePullSecrets: + - name: docker-registry-secret + appConfig: + env: prod + config_center_enabled: "false" + config_center_base_url: "" + extraEnv: + copy_pipeline: "data_transfer" + data_storage: "/core-data" + gr_queue: "gr_queue" + gr_exchange: "gr_exchange" + LOG_PATH: "/tmp/logs" + move_pipeline: "data_delete" + WORK_PATH: "/tmp/workdir" + # -- RabbitMQ -- + gm_queue_endpoint: "message-bus-greenroom.greenroom" + extraEnvYaml: + - name: gm_username + valueFrom: + secretKeyRef: + name: queue-producer-credentials + key: rabbitmq-username + - name: gm_password + valueFrom: + secretKeyRef: + name: queue-producer-credentials + key: rabbitmq-password + resources: + requests: + cpu: 10m + memory: 50Mi + limits: + cpu: 1 + memory: 1000Mi + readinessProbe: + tcpSocket: + port: 6060 + initialDelaySeconds: 5 + periodSeconds: 10 + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: "33%" diff --git a/clusters/prod/apps/queue-socketio/Chart.yaml b/clusters/prod/apps/queue-socketio/Chart.yaml new file mode 100644 index 0000000..a4965b1 --- /dev/null +++ b/clusters/prod/apps/queue-socketio/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: queue-socketio +version: 0.1.0 +dependencies: + - name: queue-service + version: "0.4.1" # synced from versions.yaml (queue-service-socketio) via make sync-versions + repository: https://pilotdataplatform.github.io/helm-charts/ diff --git a/clusters/prod/apps/queue-socketio/application.yaml b/clusters/prod/apps/queue-socketio/application.yaml new file mode 100644 index 0000000..5589084 --- /dev/null +++ b/clusters/prod/apps/queue-socketio/application.yaml @@ -0,0 +1,27 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: queue-socketio + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "8" +spec: + project: default + source: + repoURL: https://github.com/PilotDataPlatform/pilot-hdc-platform-gitops.git + targetRevision: main + path: clusters/prod/apps/queue-socketio + helm: + valueFiles: + - ../../registry.yaml + - values.yaml + destination: + server: https://kubernetes.default.svc + namespace: greenroom + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/clusters/prod/apps/queue-socketio/templates/external-secret.yaml b/clusters/prod/apps/queue-socketio/templates/external-secret.yaml new file mode 100644 index 0000000..f920b68 --- /dev/null +++ b/clusters/prod/apps/queue-socketio/templates/external-secret.yaml @@ -0,0 +1,21 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: queue-socketio-credentials + namespace: greenroom +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: vault + target: + name: queue-socketio-credentials + data: + - secretKey: rabbitmq-username + remoteRef: + key: secret/data/rabbitmq + property: username + - secretKey: rabbitmq-password + remoteRef: + key: secret/data/rabbitmq + property: password diff --git a/clusters/prod/apps/queue-socketio/values.yaml b/clusters/prod/apps/queue-socketio/values.yaml new file mode 100644 index 0000000..023d43e --- /dev/null +++ b/clusters/prod/apps/queue-socketio/values.yaml @@ -0,0 +1,64 @@ +queue-service: + image: + repository: n47w5524.c1.de1.container-registry.ovh.net/hdc-services-image/queue/socketio + pullPolicy: IfNotPresent + tag: "socketio-2.2.16" + fullnameOverride: queue-socketio + replicaCount: 1 + container: + port: 6062 + service: + type: ClusterIP + port: 6062 + targetPort: 6062 + imagePullSecrets: + - name: docker-registry-secret + appConfig: + env: prod + config_center_enabled: "false" + config_center_base_url: "" + extraEnv: + # -- RabbitMQ -- + gm_queue_endpoint: "message-bus-greenroom.greenroom" + extraEnvYaml: + - name: gm_username + valueFrom: + secretKeyRef: + name: queue-socketio-credentials + key: rabbitmq-username + - name: gm_password + valueFrom: + secretKeyRef: + name: queue-socketio-credentials + key: rabbitmq-password + resources: + requests: + cpu: 10m + memory: 50Mi + limits: + cpu: 1 + memory: 1000Mi + readinessProbe: + tcpSocket: + port: 6062 + initialDelaySeconds: 5 + periodSeconds: 10 + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: "33%" + ingress: + enabled: true + hostname: hdc.ebrains.eu + path: /socket.io/ + pathType: ImplementationSpecific + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + kubernetes.io/ingress.class: nginx + nginx.ingress.kubernetes.io/proxy-body-size: 25m + nginx.ingress.kubernetes.io/ssl-redirect: "true" + # WebSocket support + nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" + nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" + tls: true diff --git a/clusters/prod/apps/search/Chart.yaml b/clusters/prod/apps/search/Chart.yaml new file mode 100644 index 0000000..52abf81 --- /dev/null +++ b/clusters/prod/apps/search/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: search +version: 0.1.0 +dependencies: + - name: search-service + version: "0.2.2" + repository: https://pilotdataplatform.github.io/helm-charts/ diff --git a/clusters/prod/apps/search/application.yaml b/clusters/prod/apps/search/application.yaml new file mode 100644 index 0000000..82c3578 --- /dev/null +++ b/clusters/prod/apps/search/application.yaml @@ -0,0 +1,28 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: search + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "8" +spec: + project: default + source: + repoURL: https://github.com/PilotDataPlatform/pilot-hdc-platform-gitops.git + targetRevision: main + path: clusters/prod/apps/search + helm: + valueFiles: + - ../../registry.yaml + - ../../versions.yaml + - values.yaml + destination: + server: https://kubernetes.default.svc + namespace: utility + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/clusters/prod/apps/search/values.yaml b/clusters/prod/apps/search/values.yaml new file mode 100644 index 0000000..7321ad1 --- /dev/null +++ b/clusters/prod/apps/search/values.yaml @@ -0,0 +1,47 @@ +search-service: + image: + repository: n47w5524.c1.de1.container-registry.ovh.net/hdc-services-image/search + pullPolicy: IfNotPresent + + fullnameOverride: search + replicaCount: 1 + + container: + port: 5064 + + service: + type: ClusterIP + port: 5064 + targetPort: 5064 + + imagePullSecrets: + - name: docker-registry-secret + + appConfig: + env: prod + port: 5064 + config_center_enabled: false + config_center_base_url: http://common.utility:5062/ + srv_namespace: service_search + + extraEnv: + HOST: "0.0.0.0" + PORT: "5064" + ELASTICSEARCH_URI: "http://elasticsearch-master.utility:9200" + WORKSERS: "2" + + resources: + requests: + cpu: 10m + memory: 50Mi + limits: + cpu: 500m + memory: 500Mi + + readinessProbe: + tcpSocket: + port: 5064 + initialDelaySeconds: 5 + periodSeconds: 10 + failureThreshold: 3 + successThreshold: 1 diff --git a/clusters/prod/apps/upload-core/Chart.yaml b/clusters/prod/apps/upload-core/Chart.yaml new file mode 100644 index 0000000..1f2c615 --- /dev/null +++ b/clusters/prod/apps/upload-core/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: upload-core +version: 0.1.0 +dependencies: + - name: upload-service + version: "0.3.1" + repository: https://pilotdataplatform.github.io/helm-charts/ diff --git a/clusters/prod/apps/upload-core/application.yaml b/clusters/prod/apps/upload-core/application.yaml new file mode 100644 index 0000000..1c80377 --- /dev/null +++ b/clusters/prod/apps/upload-core/application.yaml @@ -0,0 +1,28 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: upload-core + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "8" +spec: + project: default + source: + repoURL: https://github.com/PilotDataPlatform/pilot-hdc-platform-gitops.git + targetRevision: main + path: clusters/prod/apps/upload-core + helm: + valueFiles: + - ../../registry.yaml + - ../../versions.yaml + - values.yaml + destination: + server: https://kubernetes.default.svc + namespace: core + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/clusters/prod/apps/upload-core/templates/external-secret.yaml b/clusters/prod/apps/upload-core/templates/external-secret.yaml new file mode 100644 index 0000000..4254aca --- /dev/null +++ b/clusters/prod/apps/upload-core/templates/external-secret.yaml @@ -0,0 +1,25 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: upload-credentials + namespace: core +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: vault + target: + name: upload-credentials + data: + - secretKey: redis-password + remoteRef: + key: secret/data/redis + property: password + - secretKey: minio-access-key + remoteRef: + key: secret/data/minio + property: access_key + - secretKey: minio-secret-key + remoteRef: + key: secret/data/minio + property: secret_key diff --git a/clusters/prod/apps/upload-core/values.yaml b/clusters/prod/apps/upload-core/values.yaml new file mode 100644 index 0000000..6425753 --- /dev/null +++ b/clusters/prod/apps/upload-core/values.yaml @@ -0,0 +1,96 @@ +upload-service: + image: + repository: n47w5524.c1.de1.container-registry.ovh.net/hdc-services-image/upload + pullPolicy: IfNotPresent + + + fullnameOverride: upload + replicaCount: 1 + + container: + port: 5079 + + service: + type: ClusterIP + port: 5079 + targetPort: 5079 + + imagePullSecrets: + - name: docker-registry-secret + + appConfig: + port: 5079 + env: prod + + extraEnv: + HOST: "0.0.0.0" + namespace: "core" + ROOT_PATH: "/data/core-storage" + CORE_ZONE_LABEL: "Core" + GREEN_ZONE_LABEL: "Greenroom" + DATAOPS_SERVICE: "http://dataops.utility:5063" + METADATA_SERVICE: "http://metadata.utility:5066" + PROJECT_SERVICE: "http://project.utility:5064" + S3_INTERNAL: "minio.minio:9000" + S3_INTERNAL_HTTPS: "False" + S3_PUBLIC: "object.hdc.ebrains.eu" + S3_PUBLIC_HTTPS: "True" + REDIS_HOST: "redis-master.redis" + REDIS_PORT: "6379" + REDIS_DB: "0" + KAFKA_URL: "kafka.utility:9092" + KAFKA_ACTIVITY_TOPIC: "metadata.items.activity" + OPEN_TELEMETRY_ENABLED: "False" + OPEN_TELEMETRY_HOST: "127.0.0.1" + OPEN_TELEMETRY_PORT: "6831" + + extraEnvYaml: + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: upload-credentials + key: redis-password + - name: S3_ACCESS_KEY + valueFrom: + secretKeyRef: + name: upload-credentials + key: minio-access-key + - name: S3_SECRET_KEY + valueFrom: + secretKeyRef: + name: upload-credentials + key: minio-secret-key + + # Single object, not array — chart template accesses .Values.volumes.mountPath directly + volumes: + name: upload-storage + mountPath: /data/core-storage + claimName: core-storage + + resources: + requests: + cpu: 10m + memory: 50Mi + limits: + cpu: 500m + memory: 500Mi + + readinessProbe: + tcpSocket: + port: 5079 + initialDelaySeconds: 5 + periodSeconds: 10 + + livenessProbe: + httpGet: + path: /v1/health + port: 5079 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 3 + + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 diff --git a/clusters/prod/apps/upload-greenroom/Chart.yaml b/clusters/prod/apps/upload-greenroom/Chart.yaml new file mode 100644 index 0000000..410f3b6 --- /dev/null +++ b/clusters/prod/apps/upload-greenroom/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: upload-greenroom +version: 0.1.0 +dependencies: + - name: upload-service + version: "0.3.1" + repository: https://pilotdataplatform.github.io/helm-charts/ diff --git a/clusters/prod/apps/upload-greenroom/application.yaml b/clusters/prod/apps/upload-greenroom/application.yaml new file mode 100644 index 0000000..98d186e --- /dev/null +++ b/clusters/prod/apps/upload-greenroom/application.yaml @@ -0,0 +1,28 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: upload-greenroom + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "8" +spec: + project: default + source: + repoURL: https://github.com/PilotDataPlatform/pilot-hdc-platform-gitops.git + targetRevision: main + path: clusters/prod/apps/upload-greenroom + helm: + valueFiles: + - ../../registry.yaml + - ../../versions.yaml + - values.yaml + destination: + server: https://kubernetes.default.svc + namespace: greenroom + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/clusters/prod/apps/upload-greenroom/templates/external-secret.yaml b/clusters/prod/apps/upload-greenroom/templates/external-secret.yaml new file mode 100644 index 0000000..08e76ef --- /dev/null +++ b/clusters/prod/apps/upload-greenroom/templates/external-secret.yaml @@ -0,0 +1,25 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: upload-credentials + namespace: greenroom +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: vault + target: + name: upload-credentials + data: + - secretKey: redis-password + remoteRef: + key: secret/data/redis + property: password + - secretKey: minio-access-key + remoteRef: + key: secret/data/minio + property: access_key + - secretKey: minio-secret-key + remoteRef: + key: secret/data/minio + property: secret_key diff --git a/clusters/prod/apps/upload-greenroom/values.yaml b/clusters/prod/apps/upload-greenroom/values.yaml new file mode 100644 index 0000000..67323ab --- /dev/null +++ b/clusters/prod/apps/upload-greenroom/values.yaml @@ -0,0 +1,96 @@ +upload-service: + image: + repository: n47w5524.c1.de1.container-registry.ovh.net/hdc-services-image/upload + pullPolicy: IfNotPresent + + + fullnameOverride: upload + replicaCount: 1 + + container: + port: 5079 + + service: + type: ClusterIP + port: 5079 + targetPort: 5079 + + imagePullSecrets: + - name: docker-registry-secret + + appConfig: + port: 5079 + env: prod + + extraEnv: + HOST: "0.0.0.0" + namespace: "greenroom" + ROOT_PATH: "/data/greenroom-storage" + CORE_ZONE_LABEL: "Core" + GREEN_ZONE_LABEL: "Greenroom" + DATAOPS_SERVICE: "http://dataops.utility:5063" + METADATA_SERVICE: "http://metadata.utility:5066" + PROJECT_SERVICE: "http://project.utility:5064" + S3_INTERNAL: "minio.minio:9000" + S3_INTERNAL_HTTPS: "False" + S3_PUBLIC: "object.hdc.ebrains.eu" + S3_PUBLIC_HTTPS: "True" + REDIS_HOST: "redis-master.redis" + REDIS_PORT: "6379" + REDIS_DB: "0" + KAFKA_URL: "kafka.utility:9092" + KAFKA_ACTIVITY_TOPIC: "metadata.items.activity" + OPEN_TELEMETRY_ENABLED: "False" + OPEN_TELEMETRY_HOST: "127.0.0.1" + OPEN_TELEMETRY_PORT: "6831" + + extraEnvYaml: + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: upload-credentials + key: redis-password + - name: S3_ACCESS_KEY + valueFrom: + secretKeyRef: + name: upload-credentials + key: minio-access-key + - name: S3_SECRET_KEY + valueFrom: + secretKeyRef: + name: upload-credentials + key: minio-secret-key + + # Single object, not array — chart template accesses .Values.volumes.mountPath directly + volumes: + name: upload-storage + mountPath: /data/greenroom-storage + claimName: greenroom-storage + + resources: + requests: + cpu: 10m + memory: 50Mi + limits: + cpu: 500m + memory: 500Mi + + readinessProbe: + tcpSocket: + port: 5079 + initialDelaySeconds: 5 + periodSeconds: 10 + + livenessProbe: + httpGet: + path: /v1/health + port: 5079 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 3 + + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 diff --git a/clusters/prod/apps/xwiki/Chart.yaml b/clusters/prod/apps/xwiki/Chart.yaml new file mode 100644 index 0000000..40b4c2a --- /dev/null +++ b/clusters/prod/apps/xwiki/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: xwiki +version: 0.1.0 +dependencies: + - name: xwiki + version: "0.3.0" + repository: https://pilotdataplatform.github.io/helm-charts/ diff --git a/clusters/prod/apps/xwiki/application.yaml b/clusters/prod/apps/xwiki/application.yaml new file mode 100644 index 0000000..93e5af3 --- /dev/null +++ b/clusters/prod/apps/xwiki/application.yaml @@ -0,0 +1,28 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: xwiki + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "8" +spec: + project: default + source: + repoURL: https://github.com/PilotDataPlatform/pilot-hdc-platform-gitops.git + targetRevision: main + path: clusters/prod/apps/xwiki + helm: + valueFiles: + - ../../registry.yaml + - ../../versions.yaml + - values.yaml + destination: + server: https://kubernetes.default.svc + namespace: utility + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true diff --git a/clusters/prod/apps/xwiki/templates/external-secret.yaml b/clusters/prod/apps/xwiki/templates/external-secret.yaml new file mode 100644 index 0000000..6288be2 --- /dev/null +++ b/clusters/prod/apps/xwiki/templates/external-secret.yaml @@ -0,0 +1,42 @@ +# Database credentials — target name MUST be "xwiki-postgresql" +# Both the PG sub-chart (via existingSecret) and the XWiki deployment +# (hardcoded -postgresql) read from this Secret +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: xwiki-postgresql + namespace: utility +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: vault + target: + name: xwiki-postgresql + data: + - secretKey: postgresql-password + remoteRef: + key: secret/data/xwiki + property: postgresql-password +--- +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: xwiki-config + namespace: utility +spec: + refreshInterval: 1h + secretStoreRef: + kind: ClusterSecretStore + name: vault + target: + name: xwiki-config + data: + - secretKey: xwiki.cfg + remoteRef: + key: secret/data/xwiki + property: xwiki-cfg + - secretKey: xwiki.properties + remoteRef: + key: secret/data/xwiki + property: xwiki-properties diff --git a/clusters/prod/apps/xwiki/templates/pvc.yaml b/clusters/prod/apps/xwiki/templates/pvc.yaml new file mode 100644 index 0000000..5a7e170 --- /dev/null +++ b/clusters/prod/apps/xwiki/templates/pvc.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: xwiki-data + namespace: utility +spec: + accessModes: + - ReadWriteMany + storageClassName: nfs-client + resources: + requests: + storage: 5Gi diff --git a/clusters/prod/apps/xwiki/values.yaml b/clusters/prod/apps/xwiki/values.yaml new file mode 100644 index 0000000..d537017 --- /dev/null +++ b/clusters/prod/apps/xwiki/values.yaml @@ -0,0 +1,80 @@ +xwiki: + replicaCount: 1 + + overrides: + image: true + + image: + name: n47w5524.c1.de1.container-registry.ovh.net/hdc-services-image/xwiki + pullPolicy: IfNotPresent + + mysql: + enabled: false + + postgresql: + enabled: true + postgresqlUsername: xwiki + postgresqlDatabase: xwiki + existingSecret: xwiki-postgresql + image: + repository: hdc-services-external/bitnami/postgresql + tag: 11.3.0-debian-9-r38 + volumePermissions: + image: + repository: hdc-services-external/bitnami/minideb + tag: stretch + persistence: + storageClass: nfs-client + size: 1Gi + + service: + type: ClusterIP + + ingress: + enabled: true + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + nginx.ingress.kubernetes.io/affinity: cookie + nginx.ingress.kubernetes.io/proxy-body-size: 100m + nginx.ingress.kubernetes.io/proxy-connect-timeout: "3600" + nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" + nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" + hostname: xwiki.hdc.ebrains.eu + path: / + pathType: Prefix + tls: true + + # Chart-managed PVC (hardcoded name "xwiki", mounts at /opt/solr/server/solr) + # Can't disable — keep small and ignore + storageClass: nfs-client + storage: 1Gi + + solr: + enabled: false + + # Volume name "xwiki-conf" avoids collision with chart built-in volume "xwiki-data" + extraVolumes: + - name: xwiki-config + secret: + secretName: xwiki-config + - name: xwiki-conf + persistentVolumeClaim: + claimName: xwiki-data + + extraVolumeMounts: + - name: xwiki-conf + mountPath: /usr/local/xwiki/data + - name: xwiki-config + mountPath: /usr/local/xwiki/data/xwiki.cfg + subPath: xwiki.cfg + - name: xwiki-config + mountPath: /usr/local/xwiki/data/xwiki.properties + subPath: xwiki.properties + + resources: + requests: + cpu: "1" + memory: 2Gi + limits: + cpu: "2" + memory: 4Gi diff --git a/docs/vault-secrets.md b/docs/vault-secrets.md index 02dd806..3fb4332 100644 --- a/docs/vault-secrets.md +++ b/docs/vault-secrets.md @@ -105,6 +105,17 @@ vault kv put secret/download \ download-key=$(openssl rand -hex 32) ``` +## XWiki (`secret/xwiki`) + +```bash +vault kv put secret/xwiki \ + postgresql-password=$(openssl rand -hex 24) \ + xwiki-cfg='' \ + xwiki-properties='' +``` + +The `xwiki-cfg` and `xwiki-properties` values are full file contents mounted into the container. Copy from the running dev instance or from backup. + ## KG Integration (`secret/kg-integration`) ```bash