From 2d54ea33397379f538068308e131aa73c7d1ff87 Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed, 25 Jun 2025 10:59:24 +0200 Subject: [PATCH 01/37] Squashed commit of the following: commit 269f9752eaba28c90aba475b8c22104a4f00b571 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Tue Jun 24 17:26:06 2025 +0200 configurable datastores commit 17c3db02a9b0ee12fc4ac52902cca78a2995675d Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Tue Jun 24 16:21:26 2025 +0200 Bump 0.2.0 & HelmDocs commit f4b0c0f6fc7cc82cd56446f8368f29d82d09eb79 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Tue Jun 24 16:18:42 2025 +0200 DOC for TLS updated commit eceea97636625da9f92e276e15d0c71ce9c69c82 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Tue Jun 24 16:13:30 2025 +0200 changing some defaults commit 4eed2f8cebff7eead8873ae552a074cd90ed5cac Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Tue Jun 24 15:39:57 2025 +0200 wait-for-elastic password fix commit e8adbeccc6f62a9f5cd075592e2d7851dc222432 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Tue Jun 24 15:39:31 2025 +0200 configmap updated for Elastic commit 55766b7ffd5319062669dde729e93f2f5209fb78 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Tue Jun 24 13:39:05 2025 +0200 defaults values changed commit 9b7a6905dbfbc208909d331c269b50568ffaf9c9 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Tue Jun 24 13:37:43 2025 +0200 some changes required for elasticsearch commit 4d10ce2a891c1aa32fc7110883a46c743006552d Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Tue Jun 24 13:37:30 2025 +0200 wait-for-elasticsearch-schema added commit ee3f0d798d69b4d68360eb221bf4945226cfe05f Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Mon Jun 23 17:32:09 2025 +0200 Update values.yaml commit 0743300b3f083360d6229bbe668ce97b9d76f6e5 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Mon Jun 23 17:30:19 2025 +0200 wait-for-schema mysql support tls commit 2d48d0dd9660dc9faad0c857e8d1e25219acc546 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Mon Jun 23 16:56:40 2025 +0200 mysql wait-for-schema supported commit 02f65931cd022fdf190e1bd301eaac7e3b13319e Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Mon Jun 23 16:08:53 2025 +0200 using latest images for wait-for-schema commit fbe533f7e83199d5ae79ccc1afa2026838451d86 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Mon Jun 23 16:01:52 2025 +0200 examples tls with mode by default commit 9f58efff3e5afca7adf4134bd2735d36addbb1e3 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Mon Jun 23 15:14:13 2025 +0200 postgresql support added commit a928305fe5c5a42a04d00a475a3ad96e23f81a99 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Mon Jun 23 12:54:52 2025 +0200 cassandra.tls.serverName default required commit 95bde5fcf784221d945d5cca5631537a6f8ba565 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Mon Jun 23 11:59:34 2025 +0200 fix volumeMount nindent commit 06ea33d1ac3f551535dfdf5ae8ac2978eb9a7b8c Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Mon Jun 23 11:41:08 2025 +0200 visibility schema for cassandra skip commit b47023d63c6fcd8a4268669393a352705de6dbee Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri Jun 20 11:36:32 2025 +0200 Update values.yaml commit b36c7a683ae613b1aaf8bd052cef9591988b59fd Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri Jun 20 11:34:40 2025 +0200 chart appVersion revert commit 000ba4255f2dda603a86c6b346c0989dfb8690ab Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri Jun 20 11:04:53 2025 +0200 Fix dynamic config issue. commit d9dd5ec7b96daf23f5f980494a9842205814a237 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu Jun 19 16:22:13 2025 +0200 fix missing end commit 301df5e6f348575ed07ba6e6bd961d4d055e49f4 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu Jun 19 16:19:18 2025 +0200 Rollback server & bump web commit d96536f9dac23c0147e8207ea91db589ace2eea1 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu Jun 19 16:19:05 2025 +0200 deployment fix credentials commit 8afcb8a91bb0682efdb95bef16ca3d708b43609d Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu Jun 19 14:06:36 2025 +0200 Update values.yaml commit 04fff002c7809c8bf6842ad87fcb3dbe674800c3 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu Jun 19 13:24:54 2025 +0200 fix advancedVisibility by default commit bb2e891e53421f27bda4ec5e8168ed64f142c196 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu Jun 19 13:20:00 2025 +0200 fix point to useEnvVars commit ca8f804ace0b5e2ba9538e800803ccbd4beb167c Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu Jun 19 13:18:27 2025 +0200 useEnvVars applied to env variables commit 21e6f3c1c316e9e46a0707ccede3e50dbd80cfc0 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu Jun 19 10:55:04 2025 +0200 fix typo and removed enableGlobalDomain commit f031a6ed670f3a83a0c9f98a821ec70b47a729a8 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu Jun 19 10:37:36 2025 +0200 Update server-deployment.yaml commit 772ea21d0aa45a9a1ae57e50c5fcc711dcfcaf2c Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu Jun 19 10:35:42 2025 +0200 no env needed commit dc42630eaebe6f0b0d60e4f8c787769b9e8bb315 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu Jun 19 10:35:23 2025 +0200 Changed to cassandra image for validation commit 0673f75a72b8bbc270881866dafed9c74ea77728 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed Jun 18 17:47:15 2025 +0200 Wait-for-Schema cassandra done commit 004f1ea427975359a76bbed3cd9e3752cedb3160 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed Jun 18 17:02:56 2025 +0200 default cassandra credentials commit 338ed20dfc3577300f697f016882a365c67dc7bc Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed Jun 18 16:51:39 2025 +0200 wait-for-schema rework 1/2 Idea of how to handle all databases in wait-for-schema Still need to change the commands for every database to check the version correctly commit 9b38b3510b3e563cb5df8b45362e8b9f3135985b Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed Jun 18 15:46:29 2025 +0200 move schema apart of cassandra commit b2c63618378576879bc30a7c5e85029b6c61f90f Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed Jun 18 15:24:23 2025 +0200 Revert "testing" This reverts commit a01ed6497874662eee361ccfdff620dfbac7c3cb. commit a01ed6497874662eee361ccfdff620dfbac7c3cb Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed Jun 18 15:23:05 2025 +0200 testing commit 56b0e2443fcb4577ae31d2e59ee705a9c5aecc5a Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed Jun 18 15:18:15 2025 +0200 default's updated commit a6b455858010b3f67c65bde1ac91bf9961497898 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed Jun 18 15:14:05 2025 +0200 simplify tls volumenes commit 8e395c8cd721aa6afb2ceca7cd4c4d8c7aed6ceb Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed Jun 18 15:08:43 2025 +0200 DB secrets managed correctly commit 42fb22c7b7ef22ef7dbe7050adca33f619034474 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed Jun 18 14:51:47 2025 +0200 auto-setup enabled again for test porpuses commit a0ed87b446d7643760fb1835d5757824c9fe1cf6 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed Jun 18 12:37:38 2025 +0200 Secrets managed by env variables commit cbc4a7618a5459935c04cd2756de3abb1875f55a Merge: f0c91c1 cd13276 Author: Cosmin Lungu <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed Jun 18 11:10:54 2025 +0200 Merge branch 'cadence-workflow:main' into advanced-configuration commit f0c91c19fb721f61e13fb5171da3b62125a7a1a8 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Tue Jun 17 09:11:13 2025 +0200 lint commit 311a08515bfb2a0d879133fe2d3955750263f90d Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Tue Jun 17 09:07:25 2025 +0200 2 env variables moved to config commit 7f6c688918b64a6eeddf87aa33025fccd8b90eaf Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Tue Jun 17 09:04:42 2025 +0200 fix line break commit 959c32984b2e4e5979864f9df43f50ce9b639d52 Merge: f5f52fa 5b2f3c6 Author: Cosmin Lungu <82363564+CosminL-DEV@users.noreply.github.com> Date: Tue Jun 17 09:01:59 2025 +0200 Merge branch 'main' into advanced-configuration commit f5f52fa1dbea3551b0072bfa4655d87f2b2d6e75 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Mon Jun 16 17:21:49 2025 +0200 dynamicConfig pointing to latest version commit 552032558f8be27f6002e0df6ad036b503eacb23 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri Jun 6 15:27:31 2025 +0200 fix TLS documentation commit 5d58fb27fa81cfaf5de77c5a37d0e6075968593d Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri Jun 6 15:26:36 2025 +0200 TLS documentation final version commit a66106a6376fdc331b94a43a6d78cb6aed817b38 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri Jun 6 15:17:44 2025 +0200 enhancement for import tls commit fe0e120f7896ce1c6b0cd58d4ef3b83f4822257c Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri Jun 6 11:21:00 2025 +0200 Refactor databases block & TLS commit db9f8fefa3599b5d4a8ead5b6d3704bb781968e8 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri Jun 6 10:49:38 2025 +0200 first version of TLS docs commit 2ed416921de1941e6031981dfbd9b4ee603ddc9c Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri Jun 6 09:56:51 2025 +0200 remove unused helper commit ef61bb9fe64c2fd5bddd121d5a53b654247a3b9f Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu Jun 5 17:41:05 2025 +0200 Update values.yaml commit 2941d5dd67471a53dd19f53d766a4ee220dae761 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu Jun 5 17:33:39 2025 +0200 lines breaking fix commit b8f770bbc9e5a80fedce0f918324c333cdb5b9a7 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu Jun 5 17:28:43 2025 +0200 kafka disabled and notes added commit cf208ecd913ad4c4151799bd2df78cbbaee1dbf2 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu Jun 5 17:21:23 2025 +0200 publicClient validated commit 1076616dccf041a044333a0994aa802d608924b7 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu Jun 5 17:18:17 2025 +0200 blobstore validated and changed default commit e7abc39b475125d4d185f6d3308b8852f4f1fb70 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu Jun 5 17:14:45 2025 +0200 archival and domainDefaults config validated commit 613dc52d93933a52543b32afadf72d5dc4229f99 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu Jun 5 16:30:43 2025 +0200 remove env useless commit d6a3edc644973eeca7b57a5febc2cbc7673b2795 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu Jun 5 16:26:03 2025 +0200 config.kafka block validated with schema commit 366b02073551a3a276c258cbd9becf1d64fc2ef4 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu Jun 5 15:44:40 2025 +0200 services block finished commit 1475d9aa81a67284bd69da1ce00452f8bf9d0550 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu Jun 5 15:22:21 2025 +0200 little re-order commit abac2365a1d7916c018ce6f6bd96ab7d6aa1f41b Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu Jun 5 15:21:36 2025 +0200 little reorder commit 0caf0bec8ba411601d082ba2a9b4a9b265c938c8 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu Jun 5 15:07:51 2025 +0200 clusterGroupMetadata updated commit fd02977ad56ff0291c2fce4f12e556c04be056b4 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu Jun 5 13:49:18 2025 +0200 final correct ringpop commit e4254fa085a7cb2ed3ffa4fb6d167ac5b5de6bd9 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu Jun 5 13:46:27 2025 +0200 Correct ringpop.broadcastAddress commit ec7d9399b12041ec2e3452239cf9270fe42f848a Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu Jun 5 13:42:56 2025 +0200 ringpop corrected commit a2579486eb9ab5a987d66d0042afc848bef18e2e Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Tue Jun 3 17:54:55 2025 +0200 Full rework of configMap commit 12419b2caf5526ddbdda6798a826ed5ded38e636 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Tue Jun 3 17:51:06 2025 +0200 Update values.yaml commit 4d793305b0653818388bdd0681a14e656d9e17bc Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Tue Jun 3 17:39:20 2025 +0200 Values.Config finished commit 899425c37e04464ecf8a5eaacb1b3ba308ac9ac5 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Tue Jun 3 15:26:29 2025 +0200 Better visibility of configuration commit 14486abab096729995ae272cce36d59e6f465db3 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Tue Jun 3 12:44:30 2025 +0200 No env needed due image change commit 29ff9fdd2bf2cc1c4ade9361fbd8ea8240d67868 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Tue Jun 3 12:43:45 2025 +0200 Using image without auto-setup commit 5f2f7f0121c5a51edf0a6282d4a34300f5fe1eb1 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Tue Jun 3 12:36:52 2025 +0200 Updated config template from cadence templates commit 749f5a5e6b228f3795e7700d582e55a6897b420a Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Mon Jun 2 16:16:05 2025 +0200 need web variable commit b7ddeff52f04e8ec1c4253cf5a979d2cb5af603f Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Mon Jun 2 15:26:05 2025 +0200 BIND_ON_IP deleted and static env in template BIND_ON_IP is not needed in kubernetes, only in dockercompose commit d82c4336dfcafe60a902d3f1f22f97d1d116e140 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Mon Jun 2 15:25:31 2025 +0200 grpcPeers in helpers for web commit 63fb0c8df652d23a4d05fb4a7ac506dc5db29f69 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Mon Jun 2 15:01:15 2025 +0200 useless variable and security disabled by default commit 45850a0d49dae25b2cdedfbc47cde71c95d98bc9 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Mon Jun 2 13:24:06 2025 +0200 fix web env commit a39649aac4aa1b0e425c1b93e8254da6852c5883 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Mon Jun 2 13:06:43 2025 +0200 Create horizontal-pod-autoscaler.yaml commit 032bfd80643e72074055adf2fc42f1ab2270f7b3 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Mon Jun 2 13:06:35 2025 +0200 remove useless helpers commit a569884d34f1f4b223b19fabf5e280d554ff2e21 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Mon Jun 2 11:12:10 2025 +0200 clean some unused helpers commit f9fafc946e4320358b220d9075be26053323f939 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Mon Jun 2 11:08:13 2025 +0200 global variables move to values commit 68837799118df9ae46ec3559fab4437c0ea437e0 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Mon Jun 2 11:03:43 2025 +0200 server-deployment first refactor commit 88e15ce6744e2a83a806b026921d9b5f501db560 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri May 30 12:30:30 2025 +0200 Renamed 3 new web services I will rename web-deployment after it's approved commit 4cd8f48239a508a5fe7cd4a256f7d344bdf40628 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri May 30 11:15:49 2025 +0200 pdb disabled by default commit caa5875f7b85758ef1517a94b2506bab5b4c643c Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri May 30 11:14:46 2025 +0200 Revert "rename pdb" This reverts commit 3bde94a65ee5e358a85952427200286ae157c7e3. commit bf7227d303287392cb00ab18765c0c6e712a81ba Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri May 30 11:14:38 2025 +0200 Revert "rename network policy" This reverts commit c999237d55c04a0889556f4610f1ff50c08040d1. commit c999237d55c04a0889556f4610f1ff50c08040d1 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri May 30 11:14:11 2025 +0200 rename network policy commit 3bde94a65ee5e358a85952427200286ae157c7e3 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri May 30 11:13:49 2025 +0200 rename pdb commit e4eee844f95ba30861ddf2e83fb6a609947b8d27 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri May 30 11:08:30 2025 +0200 Remove already split service commit 93428c4dd919e5add87ddaf84bece09b56ce185f Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri May 30 11:08:15 2025 +0200 ew changes to schema job In future PR's we will update this template correctly commit 2bd5cba01dfbc30e029012eb5945bd744108d2f8 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri May 30 11:01:24 2025 +0200 few changes to cassandra deployment commit 8c88c43dfa2066ffe97db8bc0de3d3e323218461 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri May 30 10:46:24 2025 +0200 add PDB template commit 3b8e18117066dc1b67dc5dbf7930e6265d9654b6 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri May 30 10:42:07 2025 +0200 Default minimal deployment commit 4d9c9515c81b8d765b608b5185a019c9d745a5e1 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri May 30 10:40:37 2025 +0200 add networkpolicy.yaml commit ce34d9162c9e16346db04e705b9b583a9bf2150f Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri May 30 10:38:04 2025 +0200 Improved server-configmap commit 3d013678c239b8e9be88fe54e8338307a6ce08a0 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri May 30 10:37:25 2025 +0200 ServiceMonitor honorLabels logic commit c25b5257382bd71467b4db4df68023b90f2284cc Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri May 30 10:34:26 2025 +0200 web-deployment use serviceAccount commit 4e46a88e7ed43c52be019d921da1c04b58826d0c Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri May 30 10:31:25 2025 +0200 RBAC only if serviceAccount & rca enabled commit 8c2bffc2cc413ea1cb047e8fd7a0d280d9212f1b Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri May 30 10:23:37 2025 +0200 Added headless service splitted commit 871e3c65f9b990b8eeb40485a8f5f0284a407a72 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri May 30 10:19:26 2025 +0200 frotend service split and updated commit 0b4636148cf594ef85050734d457c30a4c87d5e7 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri May 30 10:18:16 2025 +0200 Added RBAC commit 9381d7b9f577af4b8671df7470260d439a65b067 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri May 30 10:12:01 2025 +0200 Added serviceAccount commit 5ac9129e2bb85fa932319b45cb762888e93e1e36 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri May 30 09:47:54 2025 +0200 Split secrets in 2 files commit 6e06b845683073f323bc8088fc4cb4cce390add7 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri May 30 09:47:32 2025 +0200 add commented secret env commit 12f453c9415733a2abc7ef539f7eea3715a98ab1 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri May 30 09:45:44 2025 +0200 fix unclosed variable commit cdbad4911befb2e396e15ff5b5eac55ce88e94ad Merge: d4505e2 acbddfb Author: Cosmin Lungu <82363564+CosminL-DEV@users.noreply.github.com> Date: Fri May 30 08:57:40 2025 +0200 Merge branch 'cadence-workflow:main' into k8s-best-practices commit d4505e2076181d86fbf90e29ff39027f7a45899a Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu May 29 17:12:30 2025 +0200 add secrets template for secretEnv variables commit 50c86a560bb2a866963de55144beca1947372860 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu May 29 17:01:59 2025 +0200 Cadence web deployment updated commit b540d5c660fa833f95ecf89daba8e073fe9adb54 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu May 29 16:57:43 2025 +0200 removed log config for web commit b230d2871f4ac9b1049f9a015e489a3ddb7a906b Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu May 29 16:47:13 2025 +0200 securityContext block useless Using podSecurityContext & containerSecurityContext instead commit 5fb7f9de43575ea6cc78fcb9475874281a3940e5 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu May 29 16:34:31 2025 +0200 pod labels not usefull commit 6f4339c253948c87ec20114b43c3d76f5abba51e Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu May 29 16:03:37 2025 +0200 namespaceSelector is a array commit fdbe9bbc6329bb2be17126ef3396358df4340584 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu May 29 16:01:40 2025 +0200 Revert "fix typo in jobLabel" This reverts commit b4a1f29f2f5183b07231acdbe9aa85629a502437. commit b4a1f29f2f5183b07231acdbe9aa85629a502437 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu May 29 15:55:29 2025 +0200 fix typo in jobLabel commit 4e6ae05bc0259585713092e40c0fff3764540ac5 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu May 29 15:54:46 2025 +0200 cadence web ingress tested commit 61f4feba09b145c1e83567bf95f94196138561eb Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu May 29 15:51:55 2025 +0200 Cadence-web service validated commit 89302b3f28b7c97ea864a0ce229d75874c14733e Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu May 29 15:41:55 2025 +0200 probes deleted and web service added commit 79cf860871db3960468f7afac2101372fe30a81a Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu May 29 15:41:25 2025 +0200 Helpers updated commit 8de1c4b1b0eb269bb97bb8bb11f9ebe808fe5f3e Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu May 29 15:16:21 2025 +0200 Restructure values.yaml Added best practices for kubernetes. We will start to edit the templates now for use all this new parameters. commit c6ff01458de26dfa7c77925abaf5aed2fdde3642 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu May 29 14:31:41 2025 +0200 Revert "Unused file deleted" This reverts commit 5f2930a932995aedf1626dfda7a60fc3fbcc0579. commit 5f2930a932995aedf1626dfda7a60fc3fbcc0579 Author: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu May 29 14:26:17 2025 +0200 Unused file deleted --- charts/cadence/Chart.yaml | 2 +- charts/cadence/README.md | 186 ++++- charts/cadence/docs/TLS.md | 708 +++++++++++++++++ charts/cadence/templates/_helpers.tpl | 51 +- charts/cadence/templates/schema-job.yaml | 12 +- .../cadence/templates/server-configmap.yaml | 727 ++++++++++++++++++ .../cadence/templates/server-deployment.yaml | 695 ++++++++++++++++- charts/cadence/templates/server-secret.yaml | 12 +- charts/cadence/values.yaml | 568 +++++++++++++- 9 files changed, 2866 insertions(+), 95 deletions(-) create mode 100644 charts/cadence/docs/TLS.md diff --git a/charts/cadence/Chart.yaml b/charts/cadence/Chart.yaml index 28111cf..b6c2f34 100644 --- a/charts/cadence/Chart.yaml +++ b/charts/cadence/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v2 name: cadence -version: 0.1.9 +version: 0.2.0 appVersion: "1.3.1" description: | diff --git a/charts/cadence/README.md b/charts/cadence/README.md index 2c944da..562be51 100644 --- a/charts/cadence/README.md +++ b/charts/cadence/README.md @@ -1,6 +1,6 @@ # cadence -![Version: 0.1.9](https://img.shields.io/badge/Version-0.1.9-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.3.1](https://img.shields.io/badge/AppVersion-1.3.1-informational?style=flat-square) +![Version: 0.2.0](https://img.shields.io/badge/Version-0.2.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.3.1](https://img.shields.io/badge/AppVersion-1.3.1-informational?style=flat-square) Cadence is a distributed, scalable, durable, and highly available orchestration engine to execute asynchronous long-running business logic in a scalable and resilient way. @@ -33,9 +33,163 @@ This chart deploys Uber Cadence server components and web UI. | cassandra.deployment.image.tag | string | `"4.1.1"` | | | cassandra.deployment.resources | object | `{"limits":{"cpu":1,"memory":"2Gi"},"requests":{"cpu":"500m","memory":"1Gi"}}` | Resource limits and requests for Cassandra | | cassandra.endpoint | string | `""` | External Cassandra endpoint to connect to. Required when cassandra.deployment.enabled is set to false | -| cassandra.schema.version | string | `"0.40"` | Cassandra schema version of the Cadence keyspace to use | -| cassandra.schema.visibility_version | string | `"0.9"` | Cassandra schema version of the Cadence visibility keyspace to use | -| dynamicConfig.values | object | `{"history.workflowIDExternalRateLimitEnabled":[{"value":true}]}` | Dynamic config values to be set in the Cadence server List of keys can be found at https://pkg.go.dev/github.com/uber/cadence@v1.3.0/common/dynamicconfig/dynamicproperties | +| config.archival.history.enableRead | bool | `false` | Enable reading from archives | +| config.archival.history.provider | object | `{"filestore":{"dirMode":"0755","fileMode":"0644"},"gstorage":{"credentialsPath":""},"s3store":{"endpoint":"","region":"","s3ForcePathStyle":false},"type":"filestore"}` | Archive providers configuration | +| config.archival.history.provider.filestore.dirMode | string | `"0755"` | Directory mode for archive directories | +| config.archival.history.provider.filestore.fileMode | string | `"0644"` | File mode for archived files | +| config.archival.history.provider.gstorage.credentialsPath | string | `""` | Path to service account key file | +| config.archival.history.provider.s3store.endpoint | string | `""` | S3 endpoint (for S3-compatible storage) | +| config.archival.history.provider.s3store.region | string | `""` | AWS region | +| config.archival.history.provider.s3store.s3ForcePathStyle | bool | `false` | Force path style URLs | +| config.archival.history.provider.type | string | `"filestore"` | Storage type: filestore, s3, gcs | +| config.archival.history.status | string | `"disabled"` | Archival status: enabled, disabled, paused | +| config.archival.visibility.enableRead | bool | `false` | Enable reading from archives | +| config.archival.visibility.provider | object | `{"filestore":{"dirMode":"0755","fileMode":"0644"},"gstorage":{"credentialsPath":""},"s3store":{"endpoint":"","region":"","s3ForcePathStyle":false},"type":"filestore"}` | Archive providers configuration | +| config.archival.visibility.provider.filestore.dirMode | string | `"0755"` | Directory mode for archive directories | +| config.archival.visibility.provider.filestore.fileMode | string | `"0644"` | File mode for archived files | +| config.archival.visibility.provider.gstorage.credentialsPath | string | `""` | Path to service account key file | +| config.archival.visibility.provider.s3store.endpoint | string | `""` | S3 endpoint (for S3-compatible storage) | +| config.archival.visibility.provider.s3store.region | string | `""` | AWS region | +| config.archival.visibility.provider.s3store.s3ForcePathStyle | bool | `false` | Force path style URLs | +| config.archival.visibility.provider.type | string | `"filestore"` | Storage type: filestore, s3, gcs | +| config.archival.visibility.status | string | `"disabled"` | Archival status: enabled, disabled, paused | +| config.asyncWorkflowQueues.default-queue | object | `{"config":{"cluster":"default","topic":"cadence-async-wf"},"type":"kafka"}` | Async workflow queue providers | +| config.asyncWorkflowQueues.default-queue.config | object | `{"cluster":"default","topic":"cadence-async-wf"}` | Queue configuration | +| config.asyncWorkflowQueues.default-queue.config.cluster | string | `"default"` | Kafka cluster reference | +| config.asyncWorkflowQueues.default-queue.config.topic | string | `"cadence-async-wf"` | Kafka topic for async workflows | +| config.asyncWorkflowQueues.default-queue.type | string | `"kafka"` | Queue type: kafka | +| config.asyncWorkflowQueues.enabled | bool | `false` | Enable async workflow queues | +| config.blobstore.filestore.outputDirectory | string | `"/etc/cadence/blobstore"` | Output directory for blob storage | +| config.cluster.clusterGroup | string | `nil` | Cluster group configuration with additional clusters | +| config.cluster.clusterRedirectionPolicy | object | `{"policy":"noop"}` | Cluster redirection policy for cross-cluster operations | +| config.cluster.clusterRedirectionPolicy.policy | string | `"noop"` | Policy for handling cross-cluster requests (noop, selected-apis-forwarding, all-domain-apis-forwarding, selected-apis-forwarding-v2) | +| config.cluster.currentClusterName | string | `"cluster0"` | Name of the current cluster | +| config.cluster.failoverVersionIncrement | int | `10` | Version increment used during cluster failover operations | +| config.cluster.initialFailoverVersion | int | `0` | Initial failover version for this cluster | +| config.cluster.isNotPrimary | bool | `false` | Whether this cluster is not the primary cluster | +| config.cluster.primaryClusterName | string | `"cluster0"` | Name of the primary cluster in a multi-cluster setup | +| config.cluster.rpcTransport | string | `"grpc"` | RPC transport protocol (grpc or tchannel) | +| config.domainDefaults.archival | object | `{"history":{"URI":"","status":"disabled"},"visibility":{"URI":"","status":"disabled"}}` | Default archival settings for new domains - Documentation for S3 here: https://github.com/cadence-workflow/cadence/blob/v1.3.0/common/archiver/s3store/README.md - Documentation for GStorage here: https://github.com/cadence-workflow/cadence/blob/v1.3.0/common/archiver/gcloud/README.md | +| config.domainDefaults.archival.history.URI | string | `""` | Default history archival URI | +| config.domainDefaults.archival.history.status | string | `"disabled"` | Default history archival status: enabled, disabled | +| config.domainDefaults.archival.visibility.URI | string | `""` | Default visibility archival URI | +| config.domainDefaults.archival.visibility.status | string | `"disabled"` | Default visibility archival status: enabled, disabled | +| config.dynamicConfig.client | string | `"filebased"` | Dynamic config client type: noop, filebased, configstore | +| config.dynamicConfig.filebased.filepath | string | `"/etc/cadence/config/dynamicconfig/config.yaml"` | Path to dynamic config file | +| config.dynamicConfig.filebased.pollInterval | string | `"60s"` | Poll interval for config changes | +| config.kafka.brokers | string | `"kafka-service.kafka-namespace.svc.cluster.local"` | Kafka broker service. Can reference Kubernetes services | +| config.kafka.enabled | bool | `false` | Enable Kafka for async workflows | +| config.kafka.port | int | `9092` | Kafka port | +| config.kafka.sasl.enabled | bool | `false` | Enable SASL authentication | +| config.kafka.sasl.mechanism | string | `"PLAIN"` | SASL mechanism: plain, sha512 or sha256 | +| config.kafka.sasl.password | string | `""` | SASL password | +| config.kafka.sasl.username | string | `""` | SASL username | +| config.kafka.tls.caFile | string | `""` | CA certificate file to verify server certificates | +| config.kafka.tls.caFiles | list | `[]` | Multiple CA certificate files (alternative to caFile) | +| config.kafka.tls.certFile | string | `""` | Client certificate file for mutual TLS | +| config.kafka.tls.enableHostVerification | bool | `true` | Verify server hostname matches certificate | +| config.kafka.tls.enabled | bool | `false` | Enable TLS | +| config.kafka.tls.keyFile | string | `""` | Client private key file for mutual TLS | +| config.kafka.tls.requireClientAuth | bool | `false` | Require client certificate authentication | +| config.kafka.tls.serverName | string | `""` | Override server name for certificate verification | +| config.kafka.topicProperties | object | `{}` | Topic properties (optional) | +| config.kafka.visibilityDLQTopic | string | `"cadence-visibility-dlq"` | Kafka visibility DLQ topic name | +| config.kafka.visibilityTopic | string | `"cadence-visibility"` | Kafka visibility topic name | +| config.log.encoding | string | `"json"` | Log encoding format: json, console (defaults to "json") | +| config.log.level | string | `nil` | Logging level: debug, info, warn, error (inherits from global.log if not specified) | +| config.log.levelKey | string | `"level"` | Log level key name (defaults to "level") | +| config.log.outputFile | string | `""` | Output file path for logging (if stdout is false) | +| config.log.stdout | string | `nil` | Enable stdout logging (inherits from global.log if not specified) | +| config.log.useEnvVars | bool | `false` | Allow using environment variables for log configuration. If enabled, it will use ENV variable of each server component. | +| config.persistence.advancedVisibilityStore | string | `"es-visibility"` | Name of the advanced visibility datastore | +| config.persistence.database.cassandra.allowedAuthenticators | list | `[]` | Allowed authenticators for custom authentication | +| config.persistence.database.cassandra.connectAttributes | object | `{}` | Additional connection attributes | +| config.persistence.database.cassandra.connectTimeout | string | `"10s"` | Connection timeout | +| config.persistence.database.cassandra.consistency | string | `"LOCAL_QUORUM"` | Default consistency level | +| config.persistence.database.cassandra.datacenter | string | `""` | Datacenter filter for Cassandra | +| config.persistence.database.cassandra.hostSelectionPolicy | string | `"tokenaware,roundrobin"` | Host selection policy | +| config.persistence.database.cassandra.hosts | string | `"cassandra-service.cadence.svc.cluster.local"` | Cassandra hosts. Can reference Kubernetes services | +| config.persistence.database.cassandra.keyspace | string | `"cadence"` | Cassandra keyspace for main data | +| config.persistence.database.cassandra.maxConns | int | `10` | Maximum number of connections | +| config.persistence.database.cassandra.password | string | `"cassandra"` | Cassandra password | +| config.persistence.database.cassandra.port | int | `9042` | Cassandra port | +| config.persistence.database.cassandra.protoVersion | int | `4` | Cassandra protocol version | +| config.persistence.database.cassandra.region | string | `""` | AWS region filter for Cassandra (if using AWS Keyspaces) | +| config.persistence.database.cassandra.serialConsistency | string | `"LOCAL_SERIAL"` | Serial consistency level | +| config.persistence.database.cassandra.timeout | string | `"1s"` | Query timeout | +| config.persistence.database.cassandra.tls.caFile | string | `""` | CA certificate file to verify server certificates | +| config.persistence.database.cassandra.tls.caFiles | list | `[]` | Multiple CA certificate files (alternative to caFile) | +| config.persistence.database.cassandra.tls.certFile | string | `""` | Client certificate file for mutual TLS | +| config.persistence.database.cassandra.tls.enableHostVerification | bool | `true` | Verify server hostname matches certificate | +| config.persistence.database.cassandra.tls.enabled | bool | `false` | Enable TLS | +| config.persistence.database.cassandra.tls.keyFile | string | `""` | Client private key file for mutual TLS | +| config.persistence.database.cassandra.tls.requireClientAuth | bool | `false` | Require client certificate authentication | +| config.persistence.database.cassandra.tls.serverName | string | `"cassandra"` | Override server name for certificate verification | +| config.persistence.database.cassandra.user | string | `"cassandra"` | Cassandra username | +| config.persistence.database.cassandra.visibilityKeyspace | string | `"cadence_visibility"` | Cassandra keyspace for visibility data | +| config.persistence.database.driver | string | `"cassandra"` | Database driver: cassandra, mysql, postgres | +| config.persistence.database.mysql.port | int | `3306` | Default port for MySQL (overrides sql.port if specified) | +| config.persistence.database.mysql.txIsolationCompat | bool | `false` | Enable transaction isolation compatibility mode | +| config.persistence.database.postgres.port | int | `5432` | Default port for PostgreSQL (overrides sql.port if specified) | +| config.persistence.database.sql.connectAttributes | object | `{}` | Connection attributes (key-value pairs for connection string) | +| config.persistence.database.sql.dbname | string | `"cadence"` | Database name for main data | +| config.persistence.database.sql.decodingTypes | list | `["thriftrw"]` | Decoding types for SQL blobs | +| config.persistence.database.sql.encodingType | string | `"thriftrw"` | Encoding type for SQL blobs | +| config.persistence.database.sql.hosts | string | `"mysql-service.mysql-namespace.svc.cluster.local"` | Database host. Can reference Kubernetes services | +| config.persistence.database.sql.maxConnLifetime | string | `"1h"` | Maximum connection lifetime | +| config.persistence.database.sql.maxConns | int | `20` | Maximum number of connections | +| config.persistence.database.sql.maxIdleConns | int | `20` | Maximum number of idle connections | +| config.persistence.database.sql.multipleDatabasesConfig | list | `[]` | Multiple databases configuration (when useMultipleDatabases is true) | +| config.persistence.database.sql.numShards | int | `1` | Number of database shards (default: 1) | +| config.persistence.database.sql.password | string | `""` | Database password | +| config.persistence.database.sql.port | string | `nil` | Database port (will use driver default if not specified) | +| config.persistence.database.sql.tls.caFile | string | `""` | Path to CA certificate file | +| config.persistence.database.sql.tls.caFiles | list | `[]` | Multiple CA certificate files | +| config.persistence.database.sql.tls.certFile | string | `""` | Path to client certificate file | +| config.persistence.database.sql.tls.enableHostVerification | bool | `true` | Enable hostname verification (inverse of skipHostVerification) | +| config.persistence.database.sql.tls.enabled | bool | `false` | Enable TLS | +| config.persistence.database.sql.tls.keyFile | string | `""` | Path to client private key file | +| config.persistence.database.sql.tls.requireClientAuth | bool | `false` | Require client authentication for mutual TLS | +| config.persistence.database.sql.tls.serverName | string | `""` | Server name for certificate verification | +| config.persistence.database.sql.tls.sslMode | string | `""` | For MySQL: false, true, skip-verify, preferred. (Additional this should work: required, verify-ca, verify-identity) | +| config.persistence.database.sql.useMultipleDatabases | bool | `false` | Use multiple databases for sharding | +| config.persistence.database.sql.user | string | `"cadence"` | Database username | +| config.persistence.database.sql.visibilityDbname | string | `"cadence_visibility"` | Database name for visibility data | +| config.persistence.defaultStore | string | `"default"` | Name of the default datastore | +| config.persistence.elasticsearch.awsSigning | object | `{"enabled":false,"region":"","service":"es"}` | Enable AWS signing (for AWS Elasticsearch) | +| config.persistence.elasticsearch.enabled | bool | `false` | Enable Elasticsearch for advanced visibility | +| config.persistence.elasticsearch.hosts | string | `"elasticsearch-service.elastic-namespace.svc.cluster.local"` | Elasticsearch host. | +| config.persistence.elasticsearch.password | string | `""` | Elasticsearch password | +| config.persistence.elasticsearch.port | int | `9200` | Elasticsearch port | +| config.persistence.elasticsearch.protocol | string | `""` | Protocol to use (http/https). If not specified, auto-detected based on TLS settings | +| config.persistence.elasticsearch.tls.caFile | string | `""` | CA certificate file to verify server certificates | +| config.persistence.elasticsearch.tls.caFiles | list | `[]` | Multiple CA certificate files (alternative to caFile) | +| config.persistence.elasticsearch.tls.certFile | string | `""` | Client certificate file for mutual TLS | +| config.persistence.elasticsearch.tls.enableHostVerification | bool | `true` | Verify server hostname matches certificate | +| config.persistence.elasticsearch.tls.enabled | bool | `false` | Enable TLS | +| config.persistence.elasticsearch.tls.keyFile | string | `""` | Client private key file for mutual TLS | +| config.persistence.elasticsearch.tls.requireClientAuth | bool | `false` | Require client certificate authentication | +| config.persistence.elasticsearch.tls.serverName | string | `""` | Override server name for certificate verification | +| config.persistence.elasticsearch.user | string | `""` | Elasticsearch username | +| config.persistence.elasticsearch.version | string | `"v7"` | Elasticsearch version (v6, use v7 for v7 or higher) | +| config.persistence.elasticsearch.visibilityIndex | string | `"cadence-visibility"` | Elasticsearch visibility index name | +| config.persistence.enablePersistenceLatencyHistogramMetrics | bool | `false` | Enable persistence latency histogram metrics | +| config.persistence.numHistoryShards | int | `4` | Number of history shards for partitioning (CANNOT BE CHANGED ONCE SET) | +| config.persistence.visibilityStore | string | `"visibility"` | Name of the visibility datastore (basic visibility) | +| config.publicClient.hostPort | string | `""` | Frontend service address (defaults to current cluster's RPC address) | +| config.publicClient.refreshInterval | string | `"10s"` | DNS refresh interval | +| config.publicClient.transport | string | `"grpc"` | Transport protocol: grpc, tchannel | +| config.ringpop.bootstrapMode | string | `"dns"` | Bootstrap mode: dns, hosts, file | +| config.ringpop.maxJoinDuration | string | `"30s"` | Maximum duration to wait for joining the ring | +| config.ringpop.name | string | `"cadence"` | Ringpop cluster name | +| config.services.grpcMaxMsgSize | int | `4194304` | gRPC max message size | +| config.services.metrics.prometheus.timerType | string | `"histogram"` | Timer type: histogram, summary | +| config.services.metrics.statsd.endpoint | string | `""` | StatsD endpoint. Can reference Kubernetes services | +| config.services.metrics.statsd.prefixes | object | `{"frontend":"cadence-frontend","history":"cadence-history","matching":"cadence-matching","worker":"cadence-worker"}` | Metric prefixes for each service | +| config.services.metrics.type | string | `"prometheus"` | Metrics type: statsd, prometheus | +| config.services.pprof.enabled | bool | `false` | Enable pprof endpoints | +| config.services.pprof.ports | object | `{"frontend":6060,"history":6062,"matching":6061,"worker":6063}` | Pprof ports for each service | +| dynamicConfig.values | object | `{"system.minRetentionDays":[{"constraints":{},"value":0}],"system.readVisibilityStoreName":[{"value":"db"}],"system.writeVisibilityStoreName":[{"value":"db"}]}` | Dynamic config values to be set in the Cadence server List of keys can be found at https://pkg.go.dev/github.com/uber/cadence/common/dynamicconfig/dynamicproperties | | frontend.affinity | object | `{}` | Affinity rules (inherits from global if not specified) | | frontend.containerSecurityContext | object | `{}` | Container security context (inherits from global if not specified) | | frontend.env | list | `[]` | Environment variables for frontend service | @@ -58,8 +212,8 @@ This chart deploys Uber Cadence server components and web UI. | fullnameOverride | string | `nil` | Provide a name to override the full names of resources | | global.affinity | object | `{}` | Global affinity rules | | global.containerSecurityContext | object | `{}` | Global container security context | -| global.env | list | `[{"name":"ENABLE_ES","value":"false"},{"name":"SKIP_SCHEMA_SETUP","value":"true"},{"name":"RINGPOP_BOOTSTRAP_MODE","value":"dns"},{"name":"BIND_ON_IP","value":"0.0.0.0"}]` | Global environment variables (shared only by Cadence Server services [frontend, worker, matching and history]) | -| global.image | object | `{"pullPolicy":"IfNotPresent","repository":"docker.io/ubercadence/server","tag":"v1.3.1-auto-setup"}` | Global image configuration (shared only by Cadence Server services [frontend, worker, matching and history]) | +| global.env | list | `[]` | Global environment variables (shared only by Cadence Server services [frontend, worker, matching and history]) | +| global.image | object | `{"pullPolicy":"IfNotPresent","repository":"docker.io/ubercadence/server","tag":"v1.3.1"}` | Global image configuration (shared only by Cadence Server services [frontend, worker, matching and history]) | | global.imagePullSecrets | list | `[]` | Image pull secrets for private registries | | global.log | object | `{"level":"info","stdout":true}` | Global logging configuration (shared only by Cadence Server services [frontend, worker, matching and history]) | | global.log.level | string | `"info"` | Logging level (debug, info, warn, error) | @@ -68,6 +222,9 @@ This chart deploys Uber Cadence server components and web UI. | global.podSecurityContext | object | `{}` | Global pod security context | | global.priorityClassName | string | `""` | Global priority class name for pod scheduling | | global.secretEnv | list | `[]` | Global secret environment variables (shared only by Cadence Server services [frontend, worker, matching and history]) | +| global.tls | object | `{"volumeMounts":[],"volumes":[]}` | Global TLS volumes configuration | +| global.tls.volumeMounts | list | `[]` | Volume mounts for TLS certificates | +| global.tls.volumes | list | `[]` | Additional volumes for TLS certificates (The mode is important to have the minimum permissions) | | global.tolerations | list | `[]` | Global tolerations | | global.topologySpreadConstraints | list | `[]` | Global topology spread constraints | | history.affinity | object | `{}` | Affinity rules (inherits from global if not specified) | @@ -126,6 +283,21 @@ This chart deploys Uber Cadence server components and web UI. | networkPolicy.enabled | bool | `false` | Enable network policies | | networkPolicy.ingress | list | `[]` | Ingress rules | | rbac.create | bool | `false` | Enable RBAC creation | +| schema.checkSchema.cassandra.image.pullPolicy | string | `"IfNotPresent"` | | +| schema.checkSchema.cassandra.image.repository | string | `"cassandra"` | | +| schema.checkSchema.cassandra.image.tag | string | `"4.0"` | | +| schema.checkSchema.elasticsearch.image.pullPolicy | string | `"IfNotPresent"` | | +| schema.checkSchema.elasticsearch.image.repository | string | `"alpine/curl"` | | +| schema.checkSchema.elasticsearch.image.tag | string | `"latest"` | | +| schema.checkSchema.mysql.image.pullPolicy | string | `"IfNotPresent"` | | +| schema.checkSchema.mysql.image.repository | string | `"alpine/mysql"` | | +| schema.checkSchema.mysql.image.tag | string | `"latest"` | | +| schema.checkSchema.postgres.image.pullPolicy | string | `"IfNotPresent"` | | +| schema.checkSchema.postgres.image.repository | string | `"alpine/psql"` | | +| schema.checkSchema.postgres.image.tag | string | `"latest"` | | +| schema.setupJob.enabled | bool | `true` | | +| schema.version.default | string | `"0.42"` | | +| schema.version.visibility | string | `"0.9"` | | | serviceAccount.annotations | object | `{}` | Annotations for service account | | serviceAccount.automountServiceAccountToken | bool | `true` | Automatically mount service account token | | serviceAccount.create | bool | `true` | Enable service account creation | @@ -133,7 +305,7 @@ This chart deploys Uber Cadence server components and web UI. | web.affinity | object | `{}` | Affinity rules (inherits from global if not specified) | | web.containerSecurityContext | object | `{}` | Container security context (inherits from global if not specified) | | web.env | list | `[{"name":"CADENCE_WEB_PORT","value":"8088"}]` | Environment variables for web UI | -| web.image | object | `{"imagePullSecrets":[],"pullPolicy":"IfNotPresent","repository":"docker.io/ubercadence/web","tag":"v4.0.3"}` | Image configuration for Web UI | +| web.image | object | `{"imagePullSecrets":[],"pullPolicy":"IfNotPresent","repository":"docker.io/ubercadence/web","tag":"v4.0.5"}` | Image configuration for Web UI | | web.ingress.annotations | object | `{}` | Ingress annotations | | web.ingress.className | string | `""` | Ingress class name | | web.ingress.enabled | bool | `false` | Enable ingress | diff --git a/charts/cadence/docs/TLS.md b/charts/cadence/docs/TLS.md new file mode 100644 index 0000000..55dec0c --- /dev/null +++ b/charts/cadence/docs/TLS.md @@ -0,0 +1,708 @@ +# TLS Configuration for Database and Service Connections + +This document describes how to configure TLS (Transport Layer Security) for secure connections to various databases and services including Cassandra, MySQL, PostgreSQL, Elasticsearch, and Kafka. + +## Configuration Overview + +The TLS configuration supports multiple security scenarios from basic server authentication to full mutual TLS (mTLS). All services use the same TLS configuration structure and validation logic. Certificates can be provided either through direct file paths or by mounting them as Kubernetes volumes. + +### Basic Configuration + +```yaml +tls: + enabled: true + caFile: "/path/to/ca-cert.pem" + enableHostVerification: true +``` + +## Certificate Management + +### Kubernetes Volume Mounting + +For Kubernetes deployments, TLS certificates can be mounted as volumes from Secrets or ConfigMaps. This is the recommended approach for production environments as it provides better security and management. + +#### Global TLS Volume Configuration + +Configure TLS volumes at the global level to share certificates across all Cadence services: + +```yaml +global: + tls: + volumes: + # Single CA certificate from Secret + - name: database-ca-cert + secret: + secretName: database-tls-secret + items: + - key: ca.crt + path: ca.pem + mode: 0644 + + # Client certificate and key from Secret + - name: database-client-cert + secret: + secretName: database-client-secret + items: + - key: tls.crt + path: client.pem + mode: 0644 + - key: tls.key + path: client-key.pem + mode: 0600 # Restricted permissions for private key + + volumeMounts: + # Mount CA certificate + - name: database-ca-cert + mountPath: /etc/cadence/ssl/ca + readOnly: true + + # Mount client certificates + - name: database-client-cert + mountPath: /etc/cadence/ssl/client + readOnly: true +``` + +#### File Permissions and Security + +The `mode` field in Kubernetes volume items controls the file permissions of mounted certificates: + +- **`mode: 0644`**: Read-write for owner, read-only for group and others + - Use for: CA certificates, client certificates (public parts) + - Security level: Standard - suitable for non-sensitive certificate files + +- **`mode: 0600`**: Read-write for owner only, no access for group or others + - Use for: Private keys, sensitive certificate files + - Security level: Restrictive - required for private key security + +- **`mode: 0400`**: Read-only for owner, no access for group or others + - Use for: Extra security on private keys in read-only scenarios + - Security level: Maximum restriction + +**⚠️ Security Best Practice**: Always use `mode: 0600` or `mode: 0400` for private key files to prevent unauthorized access. + +#### Database-specific Examples + +```yaml +global: + tls: + volumes: + # PostgreSQL TLS certificates + - name: postgres-tls-certs + secret: + secretName: postgres-ssl-secret + items: + - key: root.crt + path: postgresql-ca.pem + mode: 0644 + - key: postgresql.crt + path: postgresql-client.pem + mode: 0644 + - key: postgresql.key + path: postgresql-client-key.pem + mode: 0600 # Private key - restricted access + + # MySQL TLS certificates + - name: mysql-tls-certs + secret: + secretName: mysql-ssl-secret + items: + - key: ca.pem + path: mysql-ca.pem + mode: 0644 + - key: client-cert.pem + path: mysql-client-cert.pem + mode: 0644 + - key: client-key.pem + path: mysql-client-key.pem + mode: 0600 # Private key - restricted access + + # Elasticsearch TLS certificates + - name: elasticsearch-tls-certs + secret: + secretName: elasticsearch-ssl-secret + items: + - key: ca.crt + path: elasticsearch-ca.pem + mode: 0644 + - key: client.crt + path: elasticsearch-client.pem + mode: 0644 + - key: client.key + path: elasticsearch-client-key.pem + mode: 0600 # Private key - restricted access + + # Kafka TLS certificates + - name: kafka-tls-certs + secret: + secretName: kafka-ssl-secret + items: + - key: ca.crt + path: kafka-ca.pem + mode: 0644 + - key: client.crt + path: kafka-client.pem + mode: 0644 + - key: client.key + path: kafka-client-key.pem + mode: 0600 # Private key - restricted access + + volumeMounts: + - name: postgres-tls-certs + mountPath: /etc/cadence/ssl/postgres + readOnly: true + - name: mysql-tls-certs + mountPath: /etc/cadence/ssl/mysql + readOnly: true + - name: elasticsearch-tls-certs + mountPath: /etc/cadence/ssl/elasticsearch + readOnly: true + - name: kafka-tls-certs + mountPath: /etc/cadence/ssl/kafka + readOnly: true +``` + +#### Multiple CA Certificates Example + +```yaml +global: + tls: + volumes: + - name: multiple-ca-certs + configMap: + name: database-ca-bundle + items: + - key: root-ca.crt + path: root-ca.pem + mode: 0644 + - key: intermediate-ca.crt + path: intermediate-ca.pem + mode: 0644 + volumeMounts: + - name: multiple-ca-certs + mountPath: /etc/cadence/ssl/ca-bundle + readOnly: true +``` + +## Configuration Parameters + +### `enabled` +- **Type**: `boolean` +- **Default**: `false` +- **Description**: Enables or disables TLS connections +- **Usage**: When `false`, all other TLS settings are ignored +- **Applies to**: All database and service connections + +### `sslMode` +- **Type**: `string` +- **Default**: `""` +- **Description**: SSL mode configuration (database-specific) +- **PostgreSQL values**: `disable`, `allow`, `prefer`, `require`, `verify-ca`, `verify-full` +- **MySQL values**: `false`, `true`, `skip-verify`, `preferred` +- **Usage**: Controls the level of SSL verification required + +### `caFile` +- **Type**: `string` +- **Default**: `""` +- **Description**: Path to Certificate Authority (CA) certificate file +- **Format**: PEM format +- **Usage**: Required to verify server certificates and establish trust +- **Example with volumes**: `"/etc/cadence/ssl/ca/ca.pem"` +- **Direct path**: `"/etc/ssl/certs/ca-certificates.crt"` +- **File permissions**: Recommended `mode: 0644` + +### `caFiles` +- **Type**: `array` +- **Default**: `[]` +- **Description**: Array of CA certificate file paths +- **Format**: PEM format +- **Usage**: Alternative to `caFile` when multiple CA certificates are needed +- **Note**: Can be used together with `caFile` - all certificates are combined +- **Example with volumes**: `["/etc/cadence/ssl/ca-bundle/root-ca.pem", "/etc/cadence/ssl/ca-bundle/intermediate-ca.pem"]` +- **File permissions**: Recommended `mode: 0644` + +### `certFile` +- **Type**: `string` +- **Default**: `""` +- **Description**: Path to client certificate file +- **Format**: PEM format +- **Usage**: Required for mutual TLS (mTLS) authentication +- **Dependencies**: Must be used together with `keyFile` +- **Example with volumes**: `"/etc/cadence/ssl/client/client.pem"` +- **File permissions**: Recommended `mode: 0644` + +### `keyFile` +- **Type**: `string` +- **Default**: `""` +- **Description**: Path to client private key file +- **Format**: PEM format (RSA or ECDSA) +- **Usage**: Required for mutual TLS (mTLS) authentication +- **Dependencies**: Must be used together with `certFile` +- **Security**: Should have restricted file permissions +- **Example with volumes**: `"/etc/cadence/ssl/client/client-key.pem"` +- **File permissions**: **Required** `mode: 0600` or `mode: 0400` + +### `enableHostVerification` +- **Type**: `boolean` +- **Default**: `true` +- **Description**: Enables hostname verification against server certificate +- **Security**: + - `true`: Verifies server certificate matches hostname (secure) + - `false`: Skips hostname verification (insecure - testing only) +- **Recommendation**: Always `true` in production environments + +### `requireClientAuth` +- **Type**: `boolean` +- **Default**: `false` +- **Description**: Requires client certificate authentication (mutual TLS) +- **Usage**: When `true`, server will request and verify client certificates +- **Dependencies**: Clients must provide `certFile` and `keyFile` +- **Server requirement**: Database/service must be configured for mTLS + +### `serverName` +- **Type**: `string` +- **Default**: `""` +- **Description**: Override server name for certificate verification +- **Usage**: Use when connecting via IP address or when certificate Common Name differs from hostname +- **Example**: `"database.example.com"` when connecting to `192.168.1.100` + +## Certificate Formats + +### PEM Format +- **Description**: Privacy-Enhanced Mail format (Base64 encoded, human-readable) +- **Extensions**: `.pem`, `.crt`, `.cer`, `.key` +- **Structure**: Contains `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----` headers +- **Support**: Only format supported by this implementation + +## Configuration Scenarios by Database/Service Type + +### 1. PostgreSQL with TLS + +```yaml +# Using Kubernetes volumes (recommended) +global: + tls: + volumes: + - name: postgres-tls-certs + secret: + secretName: postgres-ssl-secret + items: + - key: root.crt + path: postgresql-ca.pem + mode: 0644 + - key: postgresql.crt + path: postgresql-client.pem + mode: 0644 + - key: postgresql.key + path: postgresql-client-key.pem + mode: 0600 # Critical: Private key security + volumeMounts: + - name: postgres-tls-certs + mountPath: /etc/cadence/ssl/postgres + readOnly: true + +database: + sql: + driver: "postgresql" + tls: + enabled: true + sslMode: "require" + caFile: "/etc/cadence/ssl/postgres/postgresql-ca.pem" + certFile: "/etc/cadence/ssl/postgres/postgresql-client.pem" + keyFile: "/etc/cadence/ssl/postgres/postgresql-client-key.pem" + enableHostVerification: true +``` + +### 2. MySQL with TLS + +```yaml +# Using Kubernetes volumes (recommended) +global: + tls: + volumes: + - name: mysql-tls-certs + secret: + secretName: mysql-ssl-secret + items: + - key: ca.pem + path: mysql-ca.pem + mode: 0644 + - key: client-cert.pem + path: mysql-client-cert.pem + mode: 0644 + - key: client-key.pem + path: mysql-client-key.pem + mode: 0600 # Critical: Private key security + volumeMounts: + - name: mysql-tls-certs + mountPath: /etc/cadence/ssl/mysql + readOnly: true + +database: + sql: + driver: "mysql" + tls: + enabled: true + sslMode: "true" + caFile: "/etc/cadence/ssl/mysql/mysql-ca.pem" + certFile: "/etc/cadence/ssl/mysql/mysql-client-cert.pem" + keyFile: "/etc/cadence/ssl/mysql/mysql-client-key.pem" + enableHostVerification: true +``` + +### 3. Cassandra with TLS + +```yaml +# Using Kubernetes volumes (recommended) +global: + tls: + volumes: + - name: cassandra-tls-certs + secret: + secretName: cassandra-tls-secret + items: + - key: ca.crt + path: cassandra-ca.pem + mode: 0644 + - key: client.crt + path: cassandra-client.pem + mode: 0644 + - key: client.key + path: cassandra-client-key.pem + mode: 0600 # Critical: Private key security + volumeMounts: + - name: cassandra-tls-certs + mountPath: /etc/cadence/ssl/cassandra + readOnly: true + +database: + cassandra: + tls: + enabled: true + caFile: "/etc/cadence/ssl/cassandra/cassandra-ca.pem" + certFile: "/etc/cadence/ssl/cassandra/cassandra-client.pem" + keyFile: "/etc/cadence/ssl/cassandra/cassandra-client-key.pem" + enableHostVerification: true + requireClientAuth: true +``` + +### 4. Elasticsearch with TLS + +```yaml +# Using Kubernetes volumes (recommended) +global: + tls: + volumes: + - name: elasticsearch-tls-certs + secret: + secretName: elasticsearch-ssl-secret + items: + - key: ca.crt + path: elasticsearch-ca.pem + mode: 0644 + - key: client.crt + path: elasticsearch-client.pem + mode: 0644 + - key: client.key + path: elasticsearch-client-key.pem + mode: 0600 # Critical: Private key security + volumeMounts: + - name: elasticsearch-tls-certs + mountPath: /etc/cadence/ssl/elasticsearch + readOnly: true + +elasticsearch: + tls: + enabled: true + caFile: "/etc/cadence/ssl/elasticsearch/elasticsearch-ca.pem" + certFile: "/etc/cadence/ssl/elasticsearch/elasticsearch-client.pem" + keyFile: "/etc/cadence/ssl/elasticsearch/elasticsearch-client-key.pem" + enableHostVerification: true +``` + +### 5. Kafka with TLS + +```yaml +# Using Kubernetes volumes (recommended) +global: + tls: + volumes: + - name: kafka-tls-certs + secret: + secretName: kafka-ssl-secret + items: + - key: ca.crt + path: kafka-ca.pem + mode: 0644 + - key: client.crt + path: kafka-client.pem + mode: 0644 + - key: client.key + path: kafka-client-key.pem + mode: 0600 # Critical: Private key security + volumeMounts: + - name: kafka-tls-certs + mountPath: /etc/cadence/ssl/kafka + readOnly: true + +kafka: + tls: + enabled: true + caFile: "/etc/cadence/ssl/kafka/kafka-ca.pem" + certFile: "/etc/cadence/ssl/kafka/kafka-client.pem" + keyFile: "/etc/cadence/ssl/kafka/kafka-client-key.pem" + enableHostVerification: true +``` + +### 6. Multiple Services with Shared Certificates + +```yaml +# Single certificate bundle for multiple services +global: + tls: + volumes: + - name: shared-tls-bundle + secret: + secretName: shared-ssl-secret + items: + - key: ca-bundle.crt + path: ca-bundle.pem + mode: 0644 + - key: client.crt + path: client.pem + mode: 0644 + - key: client.key + path: client-key.pem + mode: 0600 # Critical: Private key security + volumeMounts: + - name: shared-tls-bundle + mountPath: /etc/cadence/ssl/shared + readOnly: true + +database: + cassandra: + tls: + enabled: true + caFile: "/etc/cadence/ssl/shared/ca-bundle.pem" + certFile: "/etc/cadence/ssl/shared/client.pem" + keyFile: "/etc/cadence/ssl/shared/client-key.pem" + enableHostVerification: true + +elasticsearch: + tls: + enabled: true + caFile: "/etc/cadence/ssl/shared/ca-bundle.pem" + certFile: "/etc/cadence/ssl/shared/client.pem" + keyFile: "/etc/cadence/ssl/shared/client-key.pem" + enableHostVerification: true + +kafka: + tls: + enabled: true + caFile: "/etc/cadence/ssl/shared/ca-bundle.pem" + certFile: "/etc/cadence/ssl/shared/client.pem" + keyFile: "/etc/cadence/ssl/shared/client-key.pem" + enableHostVerification: true +``` + +### 7. Development/Testing with Self-signed Certificates + +```yaml +# For any database/service with self-signed certificates +global: + tls: + volumes: + - name: dev-tls-certs + secret: + secretName: dev-ssl-secret + items: + - key: self-signed-ca.crt + path: self-signed-ca.pem + mode: 0644 + - key: client.crt + path: client.pem + mode: 0644 + - key: client.key + path: client-key.pem + mode: 0600 # Even in dev, protect private keys + volumeMounts: + - name: dev-tls-certs + mountPath: /etc/cadence/ssl/dev + readOnly: true + +tls: + enabled: true + caFile: "/etc/cadence/ssl/dev/self-signed-ca.pem" + enableHostVerification: false # Only if hostname doesn't match + serverName: "service.local" # If needed for verification +``` + +**⚠️ Warning**: Only use `enableHostVerification: false` in development environments + +## Security Best Practices + +### Production Environments +- **Use Kubernetes Secrets** for certificate storage instead of direct file paths +- Always use `enableHostVerification: true` +- Use certificates from trusted Certificate Authorities +- Regularly rotate certificates before expiration +- Use mutual TLS for high-security requirements +- Mount certificates as read-only volumes +- Use appropriate `sslMode` for SQL databases +- **Always set `mode: 0600` for private key files** + +### File Permission Guidelines +- **CA Certificates**: `mode: 0644` (readable by all) +- **Client Certificates**: `mode: 0644` (readable by all) +- **Private Keys**: `mode: 0600` (owner access only) or `mode: 0400` (read-only) +- **Never use**: `mode: 0777` or overly permissive settings for any certificate files + +### Database-specific Security +- **PostgreSQL**: Use `sslMode: "verify-full"` for maximum security +- **MySQL**: Use `sslMode: "true"` and verify certificates +- **Cassandra**: Enable `requireClientAuth: true` for mutual TLS +- **Elasticsearch**: Configure cluster security with proper certificates +- **Kafka**: Use SASL_SSL for authentication combined with TLS + +### Kubernetes Security +- Store certificates in Kubernetes Secrets, not ConfigMaps +- Use RBAC to restrict access to certificate secrets +- Consider using cert-manager for automated certificate lifecycle management +- Implement certificate rotation strategies +- Use different certificates for different environments +- **Ensure proper file permissions on mounted volumes** + +### Certificate Management +- Monitor certificate expiration dates +- Implement automated certificate renewal +- Keep CA certificates up to date +- Use separate certificates for different services when possible +- Consider using Kubernetes cert-manager for automation +- **Audit file permissions regularly** + +## Troubleshooting + +### Common Issues + +1. **Certificate verification failed** + - Check if CA certificate is correct for the specific service + - Verify certificate chain is complete + - Ensure certificate hasn't expired + - Verify volume mounts are correct when using Kubernetes + +2. **SSL mode issues (SQL databases)** + - Verify `sslMode` is appropriate for your database type + - Check database server SSL configuration + - Ensure SSL is enabled on the database server + +3. **Volume mount issues** + - Verify Secret/ConfigMap exists in the correct namespace + - Check that the secret keys match the volume configuration + - Ensure proper RBAC permissions for accessing secrets + - **Verify file permissions with `mode` settings** + +4. **Permission denied errors** + - Check if `mode` is set correctly for private key files + - Ensure private keys have `mode: 0600` or more restrictive + - Verify the pod's security context allows access to the files + +5. **Service-specific connection issues** + - **Cassandra**: Verify TLS port (usually 9142) is used + - **PostgreSQL**: Check `sslmode` parameter in connection string + - **MySQL**: Verify SSL parameters are correctly set + - **Elasticsearch**: Check HTTPS port and cluster security + - **Kafka**: Verify SASL_SSL configuration + +### Validation Commands + +Test certificate validity: +```bash +# Check certificate details +openssl x509 -in /path/to/cert.pem -text -noout + +# Verify certificate chain +openssl verify -CAfile /path/to/ca.pem /path/to/cert.pem + +# Test TLS connection to different services +openssl s_client -connect cassandra-host:9142 -CAfile /path/to/ca.pem +openssl s_client -connect postgres-host:5432 -CAfile /path/to/ca.pem +openssl s_client -connect mysql-host:3306 -CAfile /path/to/ca.pem +openssl s_client -connect elasticsearch-host:9200 -CAfile /path/to/ca.pem +openssl s_client -connect kafka-host:9093 -CAfile /path/to/ca.pem +``` + +Test Kubernetes volume mounts and permissions: +```bash +# Check if certificates are mounted correctly +kubectl exec -it -- ls -la /etc/cadence/ssl/ +kubectl exec -it -- cat /etc/cadence/ssl/ca.pem + +# Verify file permissions specifically +kubectl exec -it -- ls -la /etc/cadence/ssl/client/ +kubectl exec -it -- stat /etc/cadence/ssl/client/client-key.pem + +# Verify Secret contents +kubectl get secret -o yaml +kubectl describe secret + +# Test file access +kubectl exec -it -- head -n 5 /etc/cadence/ssl/client/client-key.pem +``` + +Expected file permissions output: +```bash +# CA and client certificates (should be 644) +-rw-r--r-- 1 root root 1234 Jan 01 12:00 ca.pem +-rw-r--r-- 1 root root 1234 Jan 01 12:00 client.pem + +# Private key (should be 600) +-rw------- 1 root root 1234 Jan 01 12:00 client-key.pem +``` + +## Service-specific Integration Notes + +### PostgreSQL +```yaml +# postgresql.conf +ssl = on +ssl_cert_file = 'server.crt' +ssl_key_file = 'server.key' +ssl_ca_file = 'ca.crt' +``` + +### MySQL +```yaml +# my.cnf +[mysqld] +ssl-ca=/path/to/ca.pem +ssl-cert=/path/to/server-cert.pem +ssl-key=/path/to/server-key.pem +require_secure_transport=ON +``` + +### Cassandra +```yaml +# cassandra.yaml +client_encryption_options: + enabled: true + optional: false + keystore: /path/to/server-keystore.jks + truststore: /path/to/server-truststore.jks +``` + +### Elasticsearch +```yaml +# elasticsearch.yml +xpack.security.http.ssl.enabled: true +xpack.security.http.ssl.keystore.path: /path/to/keystore.p12 +xpack.security.http.ssl.truststore.path: /path/to/truststore.p12 +``` + +### Kafka +```yaml +# server.properties +listeners=SASL_SSL://kafka:9093 +ssl.keystore.location=/path/to/kafka.server.keystore.jks +ssl.truststore.location=/path/to/kafka.server.truststore.jks +``` + +This configuration provides a secure, production-ready TLS setup for all supported databases and services with proper certificate management through Kubernetes, including secure file permission handling. \ No newline at end of file diff --git a/charts/cadence/templates/_helpers.tpl b/charts/cadence/templates/_helpers.tpl index df7175a..1b2230c 100644 --- a/charts/cadence/templates/_helpers.tpl +++ b/charts/cadence/templates/_helpers.tpl @@ -92,6 +92,43 @@ Check if HPA is enabled for a specific service {{- end }} {{- end }} +{{/* +Helper to generate database and service secrets based on configuration +Receives context as parameter +*/}} +{{- define "cadence.databaseSecrets" -}} +{{- $context := . -}} +{{- $secrets := list -}} + +{{- /* Cassandra password */ -}} +{{- if and (eq $context.Values.config.persistence.database.driver "cassandra") $context.Values.config.persistence.database.cassandra.password -}} +{{- $secrets = append $secrets (dict "name" "CASSANDRA_PASSWORD" "value" $context.Values.config.persistence.database.cassandra.password) -}} +{{- end -}} + +{{- /* MySQL password */ -}} +{{- if and (eq $context.Values.config.persistence.database.driver "mysql") $context.Values.config.persistence.database.sql.password -}} +{{- $secrets = append $secrets (dict "name" "MYSQL_PWD" "value" $context.Values.config.persistence.database.sql.password) -}} +{{- end -}} + +{{- /* PostgreSQL password */ -}} +{{- if and (eq $context.Values.config.persistence.database.driver "postgres") $context.Values.config.persistence.database.sql.password -}} +{{- $secrets = append $secrets (dict "name" "POSTGRES_PWD" "value" $context.Values.config.persistence.database.sql.password) -}} +{{- end -}} + +{{- /* Elasticsearch password */ -}} +{{- if and $context.Values.config.persistence.elasticsearch.enabled $context.Values.config.persistence.elasticsearch.password -}} +{{- $secrets = append $secrets (dict "name" "ES_PWD" "value" $context.Values.config.persistence.elasticsearch.password) -}} +{{- end -}} + +{{- /* Kafka SASL password */ -}} +{{- if and $context.Values.config.kafka.enabled $context.Values.config.kafka.sasl.enabled $context.Values.config.kafka.sasl.password -}} +{{- $secrets = append $secrets (dict "name" "SASL_PASSWORD" "value" $context.Values.config.kafka.sasl.password) -}} +{{- end -}} + +{{- /* Store secrets in a shared variable using a unique key */ -}} +{{- $_ := set $context "databaseSecrets" $secrets -}} +{{- end -}} + {/* Cadence GRPC Peers endpoint */}} @@ -99,20 +136,6 @@ Cadence GRPC Peers endpoint {{ include "cadence.fullname" . }}-frontend.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.frontend.grpcPort | default 7833 }} {{- end }} -{{/* -Generate Ringpop seeds for service discovery -*/}} -{{- define "cadence.ringpopSeeds" -}} -{{- $seeds := list }} -{{- $namespace := .Release.Namespace }} -{{- $fullname := include "cadence.fullname" . }} -{{- $seeds = append $seeds (printf "%s-frontend-headless.%s.svc.cluster.local:%d" $fullname $namespace (.Values.frontend.port | int)) }} -{{- $seeds = append $seeds (printf "%s-history-headless.%s.svc.cluster.local:%d" $fullname $namespace (.Values.history.port | int)) }} -{{- $seeds = append $seeds (printf "%s-matching-headless.%s.svc.cluster.local:%d" $fullname $namespace (.Values.matching.port | int)) }} -{{- $seeds = append $seeds (printf "%s-worker-headless.%s.svc.cluster.local:%d" $fullname $namespace (.Values.worker.port | int)) }} -{{- join "," $seeds }} -{{- end }} - {{/* Get the Cassandra endpoint */}} diff --git a/charts/cadence/templates/schema-job.yaml b/charts/cadence/templates/schema-job.yaml index bbf362b..6b18ae0 100644 --- a/charts/cadence/templates/schema-job.yaml +++ b/charts/cadence/templates/schema-job.yaml @@ -1,7 +1,8 @@ +{{- if .Values.schema.setupJob.enabled -}} apiVersion: batch/v1 kind: Job metadata: - name: cadence-schema-setup-v{{ .Values.cassandra.schema.version | replace "." "-" }} + name: cadence-schema-setup labels: {{- include "cadence.labels" . | nindent 4 }} spec: @@ -24,12 +25,8 @@ spec: '] # Check Cassandra readiness using cqlsh - name: check-cassandra-ready - {{- $globalImage := $.Values.global.image | default dict }} - {{- $serviceImage := $.Values.history.image | default dict }} - {{- $repository := $serviceImage.repository | default $globalImage.repository }} - {{- $tag := $serviceImage.tag | default $globalImage.tag }} - image: {{ $repository }}:{{ $tag }} - imagePullPolicy: Always + image: {{ $.Values.schema.checkSchema.cassandra.image.repository }}:{{ $.Values.schema.checkSchema.cassandra.image.tag }} + imagePullPolicy: {{ $.Values.schema.checkSchema.cassandra.image.pullPolicy }} command: ['sh', '-c', ' until cqlsh {{ include "cassandra.endpoint" . }} 9042 -e "SHOW VERSION"; do echo "Waiting for Cassandra to start..."; @@ -67,3 +64,4 @@ spec: cadence-cassandra-tool --ep $CASSANDRA_SEEDS -k $VISIBILITY_KEYSPACE setup-schema -v 0.0; cadence-cassandra-tool --ep $CASSANDRA_SEEDS -k $VISIBILITY_KEYSPACE update-schema -d $VISIBILITY_SCHEMA_DIR; '] +{{- end }} \ No newline at end of file diff --git a/charts/cadence/templates/server-configmap.yaml b/charts/cadence/templates/server-configmap.yaml index bc09c15..679220f 100644 --- a/charts/cadence/templates/server-configmap.yaml +++ b/charts/cadence/templates/server-configmap.yaml @@ -12,4 +12,731 @@ data: {{- else }} # Default dynamic configuration - empty config is valid {} + {{- end }} + + config_template.yaml: |- + # Log configuration + log: + {{- if .Values.config.log.useEnvVars }} + stdout: {{ "{{ default .Env.LOG_STDOUT \"" }}{{ .Values.config.log.stdout | default .Values.global.log.stdout | default true }}{{ "\" }}" }} + level: {{ "{{ default .Env.LOG_LEVEL \"" }}{{ .Values.config.log.level | default .Values.global.log.level | default "info" }}{{ "\" }}" }} + {{- else }} + stdout: {{ .Values.config.log.stdout | default .Values.global.log.stdout | default true }} + level: {{ .Values.config.log.level | default .Values.global.log.level | default "info" }} + {{- end }} + {{- if .Values.config.log.outputFile }} + outputFile: {{ .Values.config.log.outputFile | quote }} + {{- end }} + {{- if .Values.config.log.levelKey }} + levelKey: {{ .Values.config.log.levelKey }} + {{- else }} + levelKey: level + {{- end }} + {{- if .Values.config.log.encoding }} + encoding: {{ .Values.config.log.encoding }} + {{- else }} + encoding: json + {{- end }} + + # Persistence configuration + persistence: + numHistoryShards: {{ .Values.config.persistence.numHistoryShards | default 4 }} + defaultStore: {{ .Values.config.persistence.defaultStore | default "default" | quote }} + {{- if and .Values.config.persistence.visibilityStore (not .Values.config.persistence.elasticsearch.enabled) }} + visibilityStore: {{ .Values.config.persistence.visibilityStore | quote }} + {{- end }} + {{- if and .Values.config.persistence.advancedVisibilityStore .Values.config.persistence.elasticsearch.enabled }} + advancedVisibilityStore: {{ .Values.config.persistence.advancedVisibilityStore | quote }} + {{- end }} + enablePersistenceLatencyHistogramMetrics: {{ .Values.config.persistence.enablePersistenceLatencyHistogramMetrics | default false }} + + # DataStores configuration + datastores: + # Default datastore + {{ .Values.config.persistence.defaultStore | default "default" }}: + {{- if eq .Values.config.persistence.database.driver "cassandra" }} + nosql: + pluginName: "cassandra" + hosts: {{ .Values.config.persistence.database.cassandra.hosts | quote }} + {{- $port := .Values.config.persistence.database.cassandra.port | default 9042 | int }} + {{- if ne $port 9042 }} + port: {{ $port }} + {{- end }} + keyspace: {{ .Values.config.persistence.database.cassandra.keyspace | default "cadence" | quote }} + {{- if .Values.config.persistence.database.cassandra.user }} + user: {{ .Values.config.persistence.database.cassandra.user | quote }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.password }} + password: {{ `{{ .Env.CASSANDRA_PASSWORD }}` }} + {{- end }} + protoVersion: {{ .Values.config.persistence.database.cassandra.protoVersion | default 4 }} + {{- if .Values.config.persistence.database.cassandra.region }} + region: {{ .Values.config.persistence.database.cassandra.region | quote }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.datacenter }} + datacenter: {{ .Values.config.persistence.database.cassandra.datacenter | quote }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.maxConns }} + maxConns: {{ .Values.config.persistence.database.cassandra.maxConns }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.connectTimeout }} + connectTimeout: {{ .Values.config.persistence.database.cassandra.connectTimeout | quote }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.timeout }} + timeout: {{ .Values.config.persistence.database.cassandra.timeout | quote }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.consistency }} + consistency: {{ .Values.config.persistence.database.cassandra.consistency | quote }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.serialConsistency }} + serialConsistency: {{ .Values.config.persistence.database.cassandra.serialConsistency | quote }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.hostSelectionPolicy }} + hostSelectionPolicy: {{ .Values.config.persistence.database.cassandra.hostSelectionPolicy | quote }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.allowedAuthenticators }} + allowedAuthenticators: + {{- range .Values.config.persistence.database.cassandra.allowedAuthenticators }} + - {{ . | quote }} + {{- end }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.connectAttributes }} + connectAttributes: + {{- range $key, $value := .Values.config.persistence.database.cassandra.connectAttributes }} + {{ $key }}: {{ $value | quote }} + {{- end }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.tls.enabled }} + tls: + enabled: {{ .Values.config.persistence.database.cassandra.tls.enabled }} + {{- if .Values.config.persistence.database.cassandra.tls.caFile }} + caFile: {{ .Values.config.persistence.database.cassandra.tls.caFile | quote }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.tls.caFiles }} + caFiles: + {{- range .Values.config.persistence.database.cassandra.tls.caFiles }} + - {{ . | quote }} + {{- end }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.tls.certFile }} + certFile: {{ .Values.config.persistence.database.cassandra.tls.certFile | quote }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.tls.keyFile }} + keyFile: {{ .Values.config.persistence.database.cassandra.tls.keyFile | quote }} + {{- end }} + enableHostVerification: {{ .Values.config.persistence.database.cassandra.tls.enableHostVerification | default true }} + {{- if .Values.config.persistence.database.cassandra.tls.requireClientAuth }} + requireClientAuth: {{ .Values.config.persistence.database.cassandra.tls.requireClientAuth }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.tls.serverName }} + serverName: {{ .Values.config.persistence.database.cassandra.tls.serverName | quote }} + {{- end }} + {{- end }} + {{ else -}} + {{/* SQL configuration for both MySQL and PostgreSQL */}} + sql: + {{- if eq .Values.config.persistence.database.driver "mysql" }} + pluginName: "mysql" + {{- $port := .Values.config.persistence.database.mysql.port | default .Values.config.persistence.database.sql.port | default 3306 }} + connectAddr: "{{ .Values.config.persistence.database.sql.hosts }}:{{ $port }}" + {{- else if eq .Values.config.persistence.database.driver "postgres" }} + pluginName: "postgres" + {{- $port := .Values.config.persistence.database.postgres.port | default .Values.config.persistence.database.sql.port | default 5432 }} + connectAddr: "{{ .Values.config.persistence.database.sql.hosts }}:{{ $port }}" + {{- end }} + connectProtocol: "tcp" + databaseName: {{ .Values.config.persistence.database.sql.dbname | default "cadence" | quote }} + user: {{ .Values.config.persistence.database.sql.user | default "cadence" | quote }} + {{- if .Values.config.persistence.database.sql.password }} + {{- if eq .Values.config.persistence.database.driver "mysql" }} + password: {{ `{{ .Env.MYSQL_PWD }}` }} + {{- else if eq .Values.config.persistence.database.driver "postgres" }} + password: {{ `{{ .Env.POSTGRES_PWD }}` }} + {{- end }} + {{- end }} + maxConns: {{ .Values.config.persistence.database.sql.maxConns | default 20 }} + maxIdleConns: {{ .Values.config.persistence.database.sql.maxIdleConns | default 20 }} + maxConnLifetime: {{ .Values.config.persistence.database.sql.maxConnLifetime | default "1h" | quote }} + {{- if ne (.Values.config.persistence.database.sql.numShards | default 1 | int ) 1 }} + nShards: {{ .Values.config.persistence.database.sql.numShards }} + {{- end }} + {{- if .Values.config.persistence.database.sql.encodingType }} + encodingType: {{ .Values.config.persistence.database.sql.encodingType | quote }} + {{- end }} + {{- if .Values.config.persistence.database.sql.decodingTypes }} + decodingTypes: + {{- range .Values.config.persistence.database.sql.decodingTypes }} + - {{ . | quote }} + {{- end }} + {{- end }} + {{- if .Values.config.persistence.database.sql.useMultipleDatabases }} + useMultipleDatabases: {{ .Values.config.persistence.database.sql.useMultipleDatabases }} + {{- end }} + {{- if .Values.config.persistence.database.sql.multipleDatabasesConfig }} + multipleDatabasesConfig: + {{- range .Values.config.persistence.database.sql.multipleDatabasesConfig }} + - {{ . | toYaml | nindent 14 }} + {{- end }} + {{- end }} + {{- if .Values.config.persistence.database.sql.connectAttributes }} + connectAttributes: + {{- range $key, $value := .Values.config.persistence.database.sql.connectAttributes }} + {{ $key }}: {{ $value | quote }} + {{- end }} + {{- end }} + {{- if eq .Values.config.persistence.database.driver "mysql" }} + {{- if .Values.config.persistence.database.mysql.txIsolationCompat }} + txIsolationCompat: {{ .Values.config.persistence.database.mysql.txIsolationCompat }} + {{- end }} + {{- end }} + {{- if .Values.config.persistence.database.sql.tls.enabled }} + tls: + enabled: {{ .Values.config.persistence.database.sql.tls.enabled }} + {{- if .Values.config.persistence.database.sql.tls.sslMode }} + sslmode: {{ .Values.config.persistence.database.sql.tls.sslMode | quote }} + {{- end }} + {{- if .Values.config.persistence.database.sql.tls.caFile }} + caFile: {{ .Values.config.persistence.database.sql.tls.caFile | quote }} + {{- end }} + {{- if .Values.config.persistence.database.sql.tls.caFiles }} + caFiles: + {{- range .Values.config.persistence.database.sql.tls.caFiles }} + - {{ . | quote }} + {{- end }} + {{- end }} + {{- if .Values.config.persistence.database.sql.tls.certFile }} + certFile: {{ .Values.config.persistence.database.sql.tls.certFile | quote }} + {{- end }} + {{- if .Values.config.persistence.database.sql.tls.keyFile }} + keyFile: {{ .Values.config.persistence.database.sql.tls.keyFile | quote }} + {{- end }} + enableHostVerification: {{ .Values.config.persistence.database.sql.tls.enableHostVerification | default true }} + {{- if .Values.config.persistence.database.sql.tls.requireClientAuth }} + requireClientAuth: {{ .Values.config.persistence.database.sql.tls.requireClientAuth }} + {{- end }} + {{- if .Values.config.persistence.database.sql.tls.serverName }} + serverName: {{ .Values.config.persistence.database.sql.tls.serverName | quote }} + {{- end }} + {{- end }} + {{- end }} + {{- if ne .Values.config.persistence.elasticsearch.enabled true }} + # Visibility datastore + {{ if .Values.config.persistence.visibilityStore -}} + {{ .Values.config.persistence.visibilityStore | default "visibility" }}: + {{- if eq .Values.config.persistence.database.driver "cassandra" }} + nosql: + pluginName: "cassandra" + hosts: {{ .Values.config.persistence.database.cassandra.hosts | quote }} + {{- $port := .Values.config.persistence.database.cassandra.port | default 9042 | int }} + {{- if ne $port 9042 }} + port: {{ $port }} + {{- end }} + keyspace: {{ .Values.config.persistence.database.cassandra.visibilityKeyspace | default "cadence_visibility" | quote }} + {{- if .Values.config.persistence.database.cassandra.user }} + user: {{ .Values.config.persistence.database.cassandra.user | quote }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.password }} + password: {{ `{{ .Env.CASSANDRA_PASSWORD }}` }} + {{- end }} + protoVersion: {{ .Values.config.persistence.database.cassandra.protoVersion | default 4 }} + {{- if .Values.config.persistence.database.cassandra.region }} + region: {{ .Values.config.persistence.database.cassandra.region | quote }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.datacenter }} + datacenter: {{ .Values.config.persistence.database.cassandra.datacenter | quote }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.maxConns }} + maxConns: {{ .Values.config.persistence.database.cassandra.maxConns }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.connectTimeout }} + connectTimeout: {{ .Values.config.persistence.database.cassandra.connectTimeout | quote }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.timeout }} + timeout: {{ .Values.config.persistence.database.cassandra.timeout | quote }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.consistency }} + consistency: {{ .Values.config.persistence.database.cassandra.consistency | quote }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.serialConsistency }} + serialConsistency: {{ .Values.config.persistence.database.cassandra.serialConsistency | quote }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.hostSelectionPolicy }} + hostSelectionPolicy: {{ .Values.config.persistence.database.cassandra.hostSelectionPolicy | quote }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.allowedAuthenticators }} + allowedAuthenticators: + {{- range .Values.config.persistence.database.cassandra.allowedAuthenticators }} + - {{ . | quote }} + {{- end }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.connectAttributes }} + connectAttributes: + {{- range $key, $value := .Values.config.persistence.database.cassandra.connectAttributes }} + {{ $key }}: {{ $value | quote }} + {{- end }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.tls.enabled }} + tls: + enabled: {{ .Values.config.persistence.database.cassandra.tls.enabled }} + {{- if .Values.config.persistence.database.cassandra.tls.caFile }} + caFile: {{ .Values.config.persistence.database.cassandra.tls.caFile | quote }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.tls.caFiles }} + caFiles: + {{- range .Values.config.persistence.database.cassandra.tls.caFiles }} + - {{ . | quote }} + {{- end }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.tls.certFile }} + certFile: {{ .Values.config.persistence.database.cassandra.tls.certFile | quote }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.tls.keyFile }} + keyFile: {{ .Values.config.persistence.database.cassandra.tls.keyFile | quote }} + {{- end }} + enableHostVerification: {{ .Values.config.persistence.database.cassandra.tls.enableHostVerification | default true }} + {{- if .Values.config.persistence.database.cassandra.tls.requireClientAuth }} + requireClientAuth: {{ .Values.config.persistence.database.cassandra.tls.requireClientAuth }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.tls.serverName }} + serverName: {{ .Values.config.persistence.database.cassandra.tls.serverName | quote }} + {{- end }} + {{- end }} + {{ else -}} + {{/* SQL configuration for visibility store */}} + sql: + {{- if eq .Values.config.persistence.database.driver "mysql" }} + pluginName: "mysql" + {{- $port := .Values.config.persistence.database.mysql.port | default .Values.config.persistence.database.sql.port | default 3306 }} + connectAddr: "{{ .Values.config.persistence.database.sql.hosts }}:{{ $port }}" + {{- else if eq .Values.config.persistence.database.driver "postgres" }} + pluginName: "postgres" + {{- $port := .Values.config.persistence.database.postgres.port | default .Values.config.persistence.database.sql.port | default 5432 }} + connectAddr: "{{ .Values.config.persistence.database.sql.hosts }}:{{ $port }}" + {{- end }} + connectProtocol: "tcp" + databaseName: {{ .Values.config.persistence.database.sql.visibilityDbname | default "cadence_visibility" | quote }} + user: {{ .Values.config.persistence.database.sql.user | default "cadence" | quote }} + {{- if .Values.config.persistence.database.sql.password }} + {{- if eq .Values.config.persistence.database.driver "mysql" }} + password: {{ `{{ .Env.MYSQL_PWD }}` }} + {{- else if eq .Values.config.persistence.database.driver "postgres" }} + password: {{ `{{ .Env.POSTGRES_PWD }}` }} + {{- end }} + {{- end }} + maxConns: {{ .Values.config.persistence.database.sql.maxConns | default 20 }} + maxIdleConns: {{ .Values.config.persistence.database.sql.maxIdleConns | default 20 }} + maxConnLifetime: {{ .Values.config.persistence.database.sql.maxConnLifetime | default "1h" | quote }} + {{- if ne (.Values.config.persistence.database.sql.numShards | default 1 | int ) 1 }} + nShards: {{ .Values.config.persistence.database.sql.numShards }} + {{- end }} + {{- if .Values.config.persistence.database.sql.encodingType }} + encodingType: {{ .Values.config.persistence.database.sql.encodingType | quote }} + {{- end }} + {{- if .Values.config.persistence.database.sql.decodingTypes }} + decodingTypes: + {{- range .Values.config.persistence.database.sql.decodingTypes }} + - {{ . | quote }} + {{- end }} + {{- end }} + {{- if .Values.config.persistence.database.sql.useMultipleDatabases }} + useMultipleDatabases: {{ .Values.config.persistence.database.sql.useMultipleDatabases }} + {{- end }} + {{- if .Values.config.persistence.database.sql.multipleDatabasesConfig }} + multipleDatabasesConfig: + {{- range .Values.config.persistence.database.sql.multipleDatabasesConfig }} + - {{ . | toYaml | nindent 14 }} + {{- end }} + {{- end }} + {{- if .Values.config.persistence.database.sql.connectAttributes }} + connectAttributes: + {{- range $key, $value := .Values.config.persistence.database.sql.connectAttributes }} + {{ $key }}: {{ $value | quote }} + {{- end }} + {{- end }} + {{- if eq .Values.config.persistence.database.driver "mysql" }} + {{- if .Values.config.persistence.database.mysql.txIsolationCompat }} + txIsolationCompat: {{ .Values.config.persistence.database.mysql.txIsolationCompat }} + {{- end }} + {{- end }} + {{- if .Values.config.persistence.database.sql.tls.enabled }} + tls: + enabled: {{ .Values.config.persistence.database.sql.tls.enabled }} + {{- if .Values.config.persistence.database.sql.tls.sslMode }} + sslmode: {{ .Values.config.persistence.database.sql.tls.sslMode | quote }} + {{- end }} + {{- if .Values.config.persistence.database.sql.tls.caFile }} + caFile: {{ .Values.config.persistence.database.sql.tls.caFile | quote }} + {{- end }} + {{- if .Values.config.persistence.database.sql.tls.caFiles }} + caFiles: + {{- range .Values.config.persistence.database.sql.tls.caFiles }} + - {{ . | quote }} + {{- end }} + {{- end }} + {{- if .Values.config.persistence.database.sql.tls.certFile }} + certFile: {{ .Values.config.persistence.database.sql.tls.certFile | quote }} + {{- end }} + {{- if .Values.config.persistence.database.sql.tls.keyFile }} + keyFile: {{ .Values.config.persistence.database.sql.tls.keyFile | quote }} + {{- end }} + enableHostVerification: {{ .Values.config.persistence.database.sql.tls.enableHostVerification | default true }} + {{- if .Values.config.persistence.database.sql.tls.requireClientAuth }} + requireClientAuth: {{ .Values.config.persistence.database.sql.tls.requireClientAuth }} + {{- end }} + {{- if .Values.config.persistence.database.sql.tls.serverName }} + serverName: {{ .Values.config.persistence.database.sql.tls.serverName | quote }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + + {{- if .Values.config.persistence.elasticsearch.enabled }} + {{- if eq .Values.config.persistence.advancedVisibilityStore "es-visibility" }} + # Elasticsearch for advanced visibility + es-visibility: + elasticsearch: + disableSniff: {{ .Values.config.persistence.elasticsearch.disableSniff | default true }} + version: {{ .Values.config.persistence.elasticsearch.version | default "v7" | quote }} + url: + {{- if .Values.config.persistence.elasticsearch.protocol }} + scheme: {{ .Values.config.persistence.elasticsearch.protocol | default "http" }} + {{- else if and .Values.config.persistence.elasticsearch.tls.enabled (not .Values.config.persistence.elasticsearch.protocol) }} + scheme: "https" + {{- else }} + scheme: "http" + {{- end }} + host: {{ .Values.config.persistence.elasticsearch.hosts }}:{{ .Values.config.persistence.elasticsearch.port | default 9200 }} + indices: + visibility: {{ .Values.config.persistence.elasticsearch.visibilityIndex | default "cadence-visibility" | quote }} + {{- if .Values.config.persistence.elasticsearch.user }} + username: {{ .Values.config.persistence.elasticsearch.user | quote }} + {{- end }} + {{- if .Values.config.persistence.elasticsearch.password }} + password: {{ `{{ .Env.ES_PWD }}` }} + {{- end }} + {{- if .Values.config.persistence.elasticsearch.awsSigning.enabled }} + awsConfig: + enabled: {{ .Values.config.persistence.elasticsearch.awsSigning.enabled }} + region: {{ .Values.config.persistence.elasticsearch.awsSigning.region | quote }} + service: {{ .Values.config.persistence.elasticsearch.awsSigning.service | default "es" | quote }} + {{- end }} + {{- if .Values.config.persistence.elasticsearch.tls.enabled }} + tls: + enabled: {{ .Values.config.persistence.elasticsearch.tls.enabled }} + {{- if .Values.config.persistence.elasticsearch.tls.caFile }} + caFile: {{ .Values.config.persistence.elasticsearch.tls.caFile | quote }} + {{- end }} + {{- if .Values.config.persistence.elasticsearch.tls.caFiles }} + caFiles: + {{- range .Values.config.persistence.elasticsearch.tls.caFiles }} + - {{ . | quote }} + {{- end }} + {{- end }} + {{- if .Values.config.persistence.elasticsearch.tls.certFile }} + certFile: {{ .Values.config.persistence.elasticsearch.tls.certFile | quote }} + {{- end }} + {{- if .Values.config.persistence.elasticsearch.tls.keyFile }} + keyFile: {{ .Values.config.persistence.elasticsearch.tls.keyFile | quote }} + {{- end }} + {{- if .Values.config.persistence.elasticsearch.tls.serverName }} + serverName: {{ .Values.config.persistence.elasticsearch.tls.serverName | quote }} + {{- end }} + enableHostVerification: {{ .Values.config.persistence.elasticsearch.tls.enableHostVerification | default true }} + requireClientAuth: {{ .Values.config.persistence.elasticsearch.tls.requireClientAuth | default false }} + {{- end }} + {{- end }} + {{- end }} + + # Ringpop configuration + ringpop: + name: {{ .Values.config.ringpop.name | default "cadence" }} + bootstrapMode: {{ .Values.config.ringpop.bootstrapMode | default "dns" }} + bootstrapHosts: + - {{ include "cadence.fullname" . }}-frontend-headless:{{ .Values.frontend.port | default 7933 }} + - {{ include "cadence.fullname" . }}-history-headless:{{ .Values.history.port | default 7934 }} + - {{ include "cadence.fullname" . }}-matching-headless:{{ .Values.matching.port | default 7935 }} + - {{ include "cadence.fullname" . }}-worker-headless:{{ .Values.worker.port | default 7939 }} + maxJoinDuration: {{ .Values.config.ringpop.maxJoinDuration | default "30s" }} + + # Cluster configuration + clusterGroupMetadata: + failoverVersionIncrement: {{ .Values.config.cluster.failoverVersionIncrement | default 10 }} + primaryClusterName: {{ .Values.config.cluster.primaryClusterName | default "cluster0" }} + currentClusterName: {{ .Values.config.cluster.currentClusterName | default "cluster0" }} + clusterGroup: + {{ .Values.config.cluster.currentClusterName | default "cluster0" }}: + enabled: true + initialFailoverVersion: {{ .Values.config.cluster.initialFailoverVersion | default 0 }} + {{- $transport := .Values.config.cluster.rpcTransport | default "grpc" }} + {{- if eq $transport "grpc" }} + rpcAddress: {{ include "cadence.fullname" . }}-frontend:{{ .Values.frontend.grpcPort | default 7833 }} + {{- else }} + rpcAddress: {{ include "cadence.fullname" . }}-frontend:{{ .Values.frontend.port | default 7933 }} + {{- end }} + rpcTransport: {{ $transport | quote }} + {{- range $clusterName, $clusterConfig := .Values.config.cluster.clusterGroup }} + {{ $clusterName }}: + enabled: {{ $clusterConfig.enabled | default true }} + initialFailoverVersion: {{ $clusterConfig.initialFailoverVersion | default 0 }} + {{- $clusterTransport := $clusterConfig.rpcTransport | default "grpc" }} + {{- if $clusterConfig.rpcAddress }} + rpcAddress: {{ $clusterConfig.rpcAddress | quote }} + {{- else }} + {{- fail (printf "rpcAddress is required for cluster %s" $clusterName) }} + {{- end }} + rpcTransport: {{ $clusterTransport | quote }} + {{- end }} + {{- if .Values.config.cluster.clusterRedirectionPolicy }} + clusterRedirectionPolicy: + policy: {{ .Values.config.cluster.clusterRedirectionPolicy.policy | default "noop" }} + {{- end }} + + # Services configuration + services: + frontend: + rpc: + port: {{ .Values.frontend.port | default 7933 }} + grpcPort: {{ .Values.frontend.grpcPort | default 7833 }} + bindOnIP: {{ `{{ default .Env.POD_IP "0.0.0.0" }}` }} + grpcMaxMsgSize: {{ .Values.config.services.grpcMaxMsgSize | default 4194304 | int }} + metrics: + {{- if eq .Values.config.services.metrics.type "statsd" }} + statsd: + hostPort: {{ .Values.config.services.metrics.statsd.endpoint | quote }} + prefix: {{ .Values.config.services.metrics.statsd.prefixes.frontend | default "cadence-frontend" }} + {{- else if eq .Values.config.services.metrics.type "prometheus" }} + prometheus: + timerType: {{ .Values.config.services.metrics.prometheus.timerType | default "histogram" }} + listenAddress: "0.0.0.0:{{ .Values.metrics.port | default 9090 }}" + {{- end }} + {{- if .Values.config.services.pprof.enabled }} + pprof: + port: {{ .Values.config.services.pprof.ports.frontend | default 6060 }} + {{- end }} + + history: + rpc: + port: {{ .Values.history.port | default 7934 }} + grpcPort: {{ .Values.history.grpcPort | default 7834 }} + bindOnIP: {{ `{{ default .Env.POD_IP "0.0.0.0" }}` }} + grpcMaxMsgSize: {{ .Values.config.services.grpcMaxMsgSize | default 4194304 | int }} + metrics: + {{- if eq .Values.config.services.metrics.type "statsd" }} + statsd: + hostPort: {{ .Values.config.services.metrics.statsd.endpoint | quote }} + prefix: {{ .Values.config.services.metrics.statsd.prefixes.history | default "cadence-history" }} + {{- else if eq .Values.config.services.metrics.type "prometheus" }} + prometheus: + timerType: {{ .Values.config.services.metrics.prometheus.timerType | default "histogram" }} + listenAddress: "0.0.0.0:{{ .Values.metrics.port | default 9090 }}" + {{- end }} + {{- if .Values.config.services.pprof.enabled }} + pprof: + port: {{ .Values.config.services.pprof.ports.history | default 6062 }} + {{- end }} + + matching: + rpc: + port: {{ .Values.matching.port | default 7935 }} + grpcPort: {{ .Values.matching.grpcPort | default 7835 }} + bindOnIP: {{ `{{ default .Env.POD_IP "0.0.0.0" }}` }} + grpcMaxMsgSize: {{ .Values.config.services.grpcMaxMsgSize | default 4194304 | int }} + metrics: + {{- if eq .Values.config.services.metrics.type "statsd" }} + statsd: + hostPort: {{ .Values.config.services.metrics.statsd.endpoint | quote }} + prefix: {{ .Values.config.services.metrics.statsd.prefixes.matching | default "cadence-matching" }} + {{- else if eq .Values.config.services.metrics.type "prometheus" }} + prometheus: + timerType: {{ .Values.config.services.metrics.prometheus.timerType | default "histogram" }} + listenAddress: "0.0.0.0:{{ .Values.metrics.port | default 9090 }}" + {{- end }} + {{- if .Values.config.services.pprof.enabled }} + pprof: + port: {{ .Values.config.services.pprof.ports.matching | default 6061 }} + {{- end }} + + worker: + rpc: + port: {{ .Values.worker.port | default 7939 }} + bindOnIP: {{ `{{ default .Env.POD_IP "0.0.0.0" }}` }} + grpcMaxMsgSize: {{ .Values.config.services.grpcMaxMsgSize | default 4194304 | int }} + metrics: + {{- if eq .Values.config.services.metrics.type "statsd" }} + statsd: + hostPort: {{ .Values.config.services.metrics.statsd.endpoint | quote }} + prefix: {{ .Values.config.services.metrics.statsd.prefixes.worker | default "cadence-worker" }} + {{- else if eq .Values.config.services.metrics.type "prometheus" }} + prometheus: + timerType: {{ .Values.config.services.metrics.prometheus.timerType | default "histogram" }} + listenAddress: "0.0.0.0:{{ .Values.metrics.port | default 9090 }}" + {{- end }} + {{- if .Values.config.services.pprof.enabled }} + pprof: + port: {{ .Values.config.services.pprof.ports.worker | default 6063 }} + {{ end }} + + {{ if .Values.config.kafka.enabled -}} + # Kafka configuration + kafka: + tls: + enabled: {{ .Values.config.kafka.tls.enabled | default false }} + {{- if .Values.config.kafka.tls.caFile }} + caFile: {{ .Values.config.kafka.tls.caFile | quote }} + {{- end }} + {{- if .Values.config.kafka.tls.caFiles }} + caFiles: + {{- range .Values.config.kafka.tls.caFiles }} + - {{ . | quote }} + {{- end }} + {{- end }} + {{- if .Values.config.kafka.tls.certFile }} + certFile: {{ .Values.config.kafka.tls.certFile | quote }} + {{- end }} + {{- if .Values.config.kafka.tls.keyFile }} + keyFile: {{ .Values.config.kafka.tls.keyFile | quote }} + {{- end }} + {{- if .Values.config.kafka.tls.serverName }} + serverName: {{ .Values.config.kafka.tls.serverName | quote }} + {{- end }} + enableHostVerification: {{ .Values.config.kafka.tls.enableHostVerification | default true }} + requireClientAuth: {{ .Values.config.kafka.tls.requireClientAuth | default false }} + sasl: + enabled: {{ .Values.config.kafka.sasl.enabled | default false }} + {{- if .Values.config.kafka.sasl.enabled }} + algorithm: {{ .Values.config.kafka.sasl.mechanism | default "PLAIN" | quote }} + {{- if .Values.config.kafka.sasl.username }} + username: {{ .Values.config.kafka.sasl.username | quote }} + {{- end }} + {{- if .Values.config.kafka.sasl.password }} + password: {{ `{{ .Env.SASL_PASSWORD }}` }} + {{- end }} + {{- end }} + clusters: + default: + brokers: + - {{ .Values.config.kafka.brokers }}:{{ .Values.config.kafka.port | default 9092 }} + topics: + {{ .Values.config.kafka.visibilityTopic | default "cadence-visibility" }}: + cluster: default + {{- if .Values.config.kafka.topicProperties }} + properties: + {{- toYaml .Values.config.kafka.topicProperties | nindent 8 }} + {{- end }} + {{ .Values.config.kafka.visibilityDLQTopic | default "cadence-visibility-dlq" }}: + cluster: default + {{- if .Values.config.kafka.topicProperties }} + properties: + {{- toYaml .Values.config.kafka.topicProperties | nindent 8 }} + {{- end }} + applications: + visibility: + topic: {{ .Values.config.kafka.visibilityTopic | default "cadence-visibility" }} + dlq-topic: {{ .Values.config.kafka.visibilityDLQTopic | default "cadence-visibility-dlq" }} + {{ end }} + + # Archival configuration + archival: + history: + status: {{ .Values.config.archival.history.status | default "disabled" | quote }} + enableRead: {{ .Values.config.archival.history.enableRead | default false }} + provider: + {{- if eq .Values.config.archival.history.provider.type "filestore" }} + filestore: + fileMode: {{ .Values.config.archival.history.provider.filestore.fileMode | default "0644" | quote }} + dirMode: {{ .Values.config.archival.history.provider.filestore.dirMode | default "0755" | quote }} + {{- else if eq .Values.config.archival.history.provider.type "s3" }} + s3store: + region: {{ .Values.config.archival.history.provider.s3store.region | quote }} + {{- if .Values.config.archival.history.provider.s3store.endpoint }} + endpoint: {{ .Values.config.archival.history.provider.s3store.endpoint | quote }} + {{- end }} + s3ForcePathStyle: {{ .Values.config.archival.history.provider.s3store.s3ForcePathStyle | default false }} + {{- else if eq .Values.config.archival.history.provider.type "gcs" }} + gstorage: + credentialsPath: {{ .Values.config.archival.history.provider.gstorage.credentialsPath | quote }} + {{- end }} + visibility: + status: {{ .Values.config.archival.visibility.status | default "disabled" | quote }} + enableRead: {{ .Values.config.archival.visibility.enableRead | default false }} + provider: + {{- if eq .Values.config.archival.visibility.provider.type "filestore" }} + filestore: + fileMode: {{ .Values.config.archival.visibility.provider.filestore.fileMode | default "0644" | quote }} + dirMode: {{ .Values.config.archival.visibility.provider.filestore.dirMode | default "0755" | quote }} + {{- else if eq .Values.config.archival.visibility.provider.type "s3" }} + s3store: + region: {{ .Values.config.archival.visibility.provider.s3store.region | quote }} + {{- if .Values.config.archival.visibility.provider.s3store.endpoint }} + endpoint: {{ .Values.config.archival.visibility.provider.s3store.endpoint | quote }} + {{- end }} + s3ForcePathStyle: {{ .Values.config.archival.visibility.provider.s3store.s3ForcePathStyle | default false }} + {{- else if eq .Values.config.archival.history.provider.type "gcs" }} + gstorage: + credentialsPath: {{ .Values.config.archival.visibility.provider.gstorage.credentialsPath | quote }} + {{- end }} + + # Domain defaults configuration + domainDefaults: + archival: + history: + status: {{ .Values.config.domainDefaults.archival.history.status | default "disabled" | quote }} + {{- if .Values.config.domainDefaults.archival.history.URI }} + URI: {{ .Values.config.domainDefaults.archival.history.URI | quote }} + {{- end }} + visibility: + status: {{ .Values.config.domainDefaults.archival.visibility.status | default "disabled" | quote }} + {{- if .Values.config.domainDefaults.archival.visibility.URI }} + URI: {{ .Values.config.domainDefaults.archival.visibility.URI | quote }} + {{- end }} + + # Blobstore configuration + {{- if .Values.config.blobstore }} + blobstore: + {{- if .Values.config.blobstore.filestore }} + filestore: + outputDirectory: {{ .Values.config.blobstore.filestore.outputDirectory | default "/var/lib/cadence/blobstore" | quote }} + {{- end }} + {{- end }} + + # Public client configuration + {{- if .Values.config.publicClient }} + publicClient: + {{- if .Values.config.publicClient.hostPort }} + hostPort: {{ .Values.config.publicClient.hostPort | quote }} + {{- end }} + transport: {{ .Values.config.publicClient.transport | default "grpc" | quote }} + RefreshInterval: {{ .Values.config.publicClient.refreshInterval | default "10s" | quote }} + {{- end }} + + # Dynamic configuration + {{- if .Values.config.dynamicConfig }} + dynamicconfig: + client: {{ .Values.config.dynamicConfig.client | default "filebased" | quote }} + {{- if eq .Values.config.dynamicConfig.client "filebased" }} + filebased: + filepath: {{ .Values.config.dynamicConfig.filebased.filepath | default "/etc/cadence/config/dynamicconfig/config.yaml" | quote }} + pollInterval: {{ .Values.config.dynamicConfig.filebased.pollInterval | default "60s" | quote }} + {{- end }} + {{- end }} + + {{ if .Values.config.asyncWorkflowQueues.enabled -}} + # Async workflow queues configuration + asyncWorkflowQueues: + {{- range $queueName, $queue := .Values.config.asyncWorkflowQueues }} + {{- if ne $queueName "enabled" }} + {{ $queueName }}: + type: {{ $queue.type | default "kafka" | quote }} + {{- if $queue.config }} + config: + {{- if $queue.config.topic }} + topic: {{ $queue.config.topic | quote }} + {{- end }} + {{- if $queue.config.cluster }} + cluster: {{ $queue.config.cluster | quote }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} {{- end }} \ No newline at end of file diff --git a/charts/cadence/templates/server-deployment.yaml b/charts/cadence/templates/server-deployment.yaml index b08c62a..02775b1 100644 --- a/charts/cadence/templates/server-deployment.yaml +++ b/charts/cadence/templates/server-deployment.yaml @@ -77,28 +77,651 @@ spec: {{- toYaml $serviceTopologySpreadConstraints | nindent 8 }} {{- end }} initContainers: - - name: wait-for-schema - {{- $globalImage := $.Values.global.image | default dict }} - {{- $serviceImage := $service.image | default dict }} - {{- $repository := $serviceImage.repository | default $globalImage.repository }} - {{- $tag := $serviceImage.tag | default $globalImage.tag }} - image: {{ $repository }}:{{ $tag }} - {{- $pullPolicy := $serviceImage.pullPolicy | default $globalImage.pullPolicy | default "IfNotPresent" }} - imagePullPolicy: {{ $pullPolicy }} - command: ["sh", "-c", " - until cqlsh $CASSANDRA_ENDPOINT 9042 -e \" - USE cadence; - SELECT curr_version FROM schema_version WHERE keyspace_name = 'cadence';\" | grep -q {{ $.Values.cassandra.schema.version }} && - cqlsh $CASSANDRA_ENDPOINT 9042 -e \" - USE cadence_visibility; - SELECT curr_version FROM schema_version WHERE keyspace_name = 'cadence_visibility';\" | grep -q {{ $.Values.cassandra.schema.visibility_version }}; + - name: wait-for-schema + {{- $dbDriver := $.Values.config.persistence.database.driver }} + {{- if eq $dbDriver "cassandra" }} + image: {{ $.Values.schema.checkSchema.cassandra.image.repository }}:{{ $.Values.schema.checkSchema.cassandra.image.tag }} + imagePullPolicy: {{ $.Values.schema.checkSchema.cassandra.image.pullPolicy }} + command: + - sh + - -c + - | + # Create .cassandra directory for cqlshrc if it doesn't exist + mkdir -p ~/.cassandra + + # Build cqlshrc configuration file + cat > ~/.cassandra/cqlshrc << EOF + [connection] + hostname = $DB_HOST + port = $DB_PORT + + EOF + + # Add authentication section if user is provided + if [ -n "$DB_USER" ] && [ "$DB_USER" != "" ]; then + cat >> ~/.cassandra/cqlshrc << EOF + [authentication] + username = $DB_USER + EOF + # Add password if provided + if [ -n "$CASSANDRA_PASSWORD" ] && [ "$CASSANDRA_PASSWORD" != "" ]; then + cat >> ~/.cassandra/cqlshrc << EOF + password = $CASSANDRA_PASSWORD + EOF + fi + fi + + # Add SSL configuration if enabled + if [ "$TLS_ENABLED" = "true" ]; then + cat >> ~/.cassandra/cqlshrc << EOF + + [ssl] + EOF + # Add certificate file if specified (CA certificate) + if [ -n "$SSL_CERTFILE" ] && [ "$SSL_CERTFILE" != "" ]; then + cat >> ~/.cassandra/cqlshrc << EOF + certfile = $SSL_CERTFILE + EOF + fi + + # Add client certificate for mutual TLS + if [ -n "$SSL_CLIENT_CERT" ] && [ "$SSL_CLIENT_CERT" != "" ]; then + cat >> ~/.cassandra/cqlshrc << EOF + usercert = $SSL_CLIENT_CERT + EOF + fi + + # Add client private key for mutual TLS + if [ -n "$SSL_CLIENT_KEY" ] && [ "$SSL_CLIENT_KEY" != "" ]; then + cat >> ~/.cassandra/cqlshrc << EOF + userkey = $SSL_CLIENT_KEY + EOF + fi + + # Add validate setting + if [ -n "$SSL_VALIDATE" ] && [ "$SSL_VALIDATE" != "" ]; then + cat >> ~/.cassandra/cqlshrc << EOF + validate = $SSL_VALIDATE + EOF + else + cat >> ~/.cassandra/cqlshrc << EOF + validate = true + EOF + fi + fi + + # Debug: Show generated cqlshrc (remove in production) + echo "Generated cqlshrc:" + cat ~/.cassandra/cqlshrc + echo "---" + + # Build cqlsh command + build_cqlsh_cmd() { + local cmd="cqlsh" + + # Add SSL option if enabled + if [ "$TLS_ENABLED" = "true" ]; then + cmd="$cmd --ssl" + fi + + echo "$cmd" + } + + # Compare database version with cadence schema version + until $(build_cqlsh_cmd) -e " + USE $DB_NAME; + SELECT curr_version FROM schema_version WHERE keyspace_name = '$DB_NAME';" | grep -q "$DEFAULT_VERSION" && + { + if [ "$ES_ENABLED" = "false" ]; then + $(build_cqlsh_cmd) -e " + USE $DB_VISIBILITY_NAME; + SELECT curr_version FROM schema_version WHERE keyspace_name = '$DB_VISIBILITY_NAME';" | grep -q "$VISIBILITY_VERSION" + else + true + fi + } do - echo waiting for both cadence and cadence_visibility schema setup; - sleep 10; - done"] - env: - - name: CASSANDRA_ENDPOINT - value: {{ include "cassandra.endpoint" $ }} + echo 'Waiting for Cassandra schema to be ready...' + sleep 10 + done + env: + # Schema version parameters + - name: DEFAULT_VERSION + value: {{ $.Values.schema.version.default | quote }} + - name: VISIBILITY_VERSION + value: {{ $.Values.schema.version.visibility | quote }} + - name: ES_ENABLED + value: {{ $.Values.config.persistence.elasticsearch.enabled | quote }} + # Basic connection parameters + - name: DB_HOST + value: {{ $.Values.config.persistence.database.cassandra.hosts | quote }} + - name: DB_PORT + value: {{ $.Values.config.persistence.database.cassandra.port | quote }} + - name: DB_NAME + value: {{ $.Values.config.persistence.database.cassandra.keyspace | quote }} + - name: DB_VISIBILITY_NAME + value: {{ $.Values.config.persistence.database.cassandra.visibilityKeyspace | quote }} + # Authentication parameters (conditional) + {{- if $.Values.config.persistence.database.cassandra.user }} + - name: DB_USER + value: {{ $.Values.config.persistence.database.cassandra.user | quote }} + {{- end }} + {{- if $.Values.config.persistence.database.cassandra.password }} + - name: CASSANDRA_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "cadence.fullname" $ }}-{{ $serviceName }}-secrets + key: CASSANDRA_PASSWORD + {{- end }} + # TLS Configuration + - name: TLS_ENABLED + value: {{ $.Values.config.persistence.database.cassandra.tls.enabled | quote }} + {{- if $.Values.config.persistence.database.cassandra.tls.enabled }} + # SSL_CERTFILE environment variable (CA certificate) + {{- if $.Values.config.persistence.database.cassandra.tls.caFile }} + - name: SSL_CERTFILE + value: {{ $.Values.config.persistence.database.cassandra.tls.caFile | quote }} + {{- else if $.Values.config.persistence.database.cassandra.tls.caFiles }} + - name: SSL_CERTFILE + value: {{ index $.Values.config.persistence.database.cassandra.tls.caFiles 0 | quote }} + {{- end }} + # Client certificate for mutual TLS + {{- if $.Values.config.persistence.database.cassandra.tls.certFile }} + - name: SSL_CLIENT_CERT + value: {{ $.Values.config.persistence.database.cassandra.tls.certFile | quote }} + {{- end }} + # Client private key for mutual TLS + {{- if $.Values.config.persistence.database.cassandra.tls.keyFile }} + - name: SSL_CLIENT_KEY + value: {{ $.Values.config.persistence.database.cassandra.tls.keyFile | quote }} + {{- end }} + # SSL_VALIDATE environment variable + - name: SSL_VALIDATE + value: {{ $.Values.config.persistence.database.cassandra.tls.enableHostVerification | quote }} + {{- end }} + {{- else if eq $dbDriver "postgres" }} + image: {{ $.Values.schema.checkSchema.postgres.image.repository }}:{{ $.Values.schema.checkSchema.postgres.image.tag }} + imagePullPolicy: {{ $.Values.schema.checkSchema.postgres.image.pullPolicy }} + command: + - sh + - -c + - | + # Build connection string based on TLS configuration + build_psql_cmd() { + local cmd="psql -h $DB_HOST -p $DB_PORT -U $DB_USER" + + # Add SSL mode if TLS is enabled + if [ "$TLS_ENABLED" = "true" ] && [ -n "$SSL_MODE" ]; then + cmd="$cmd --set=sslmode=$SSL_MODE" + + # Add SSL certificate parameters if provided + if [ -n "$SSL_CERTFILE" ]; then + cmd="$cmd --set=sslrootcert=$SSL_CERTFILE" + fi + + if [ -n "$SSL_CLIENT_CERT" ]; then + cmd="$cmd --set=sslcert=$SSL_CLIENT_CERT" + fi + + if [ -n "$SSL_CLIENT_KEY" ]; then + cmd="$cmd --set=sslkey=$SSL_CLIENT_KEY" + fi + fi + + echo "$cmd" + } + + # Wait for PostgreSQL to be ready + echo "Waiting for PostgreSQL to be ready..." + until pg_isready -h $DB_HOST -p $DB_PORT -U $DB_USER; do + echo 'PostgreSQL is not ready yet...' + sleep 5 + done + echo "PostgreSQL is ready!" + + # Check schema versions in both databases + echo "Checking schema versions..." + until + # Check main database schema version + PGPASSWORD=$POSTGRES_PWD $(build_psql_cmd) -d $DB_NAME -t -c " + SELECT curr_version FROM schema_version WHERE db_name = '$DB_NAME';" | grep -q "$DEFAULT_VERSION" && + + # Check visibility database schema version (only if ES is not enabled) + { + if [ "$ES_ENABLED" = "false" ]; then + PGPASSWORD=$POSTGRES_PWD $(build_psql_cmd) -d $DB_VISIBILITY_NAME -t -c " + SELECT curr_version FROM schema_version WHERE db_name = '$DB_VISIBILITY_NAME';" | grep -q "$VISIBILITY_VERSION" + else + true + fi + } + do + echo 'Waiting for PostgreSQL schema to be ready...' + sleep 10 + done + + echo "PostgreSQL schema is ready!" + env: + # Schema version parameters + - name: DEFAULT_VERSION + value: {{ $.Values.schema.version.default | quote }} + - name: VISIBILITY_VERSION + value: {{ $.Values.schema.version.visibility | quote }} + - name: ES_ENABLED + value: {{ $.Values.config.persistence.elasticsearch.enabled | quote }} + # Basic connection parameters + - name: DB_HOST + value: {{ $.Values.config.persistence.database.sql.hosts | quote }} + - name: DB_PORT + value: {{ $.Values.config.persistence.database.postgres.port | default $.Values.config.persistence.database.sql.port | default 5432 | quote }} + - name: DB_NAME + value: {{ $.Values.config.persistence.database.sql.dbname | quote }} + - name: DB_VISIBILITY_NAME + value: {{ $.Values.config.persistence.database.sql.visibilityDbname | quote }} + - name: DB_USER + value: {{ $.Values.config.persistence.database.sql.user | quote }} + # Authentication parameters + - name: POSTGRES_PWD + valueFrom: + secretKeyRef: + name: {{ include "cadence.fullname" $ }}-{{ $serviceName }}-secrets + key: POSTGRES_PWD + # TLS Configuration + - name: TLS_ENABLED + value: {{ $.Values.config.persistence.database.sql.tls.enabled | quote }} + {{- if $.Values.config.persistence.database.sql.tls.enabled }} + # SSL Mode + - name: SSL_MODE + value: {{ $.Values.config.persistence.database.sql.tls.sslMode | default "require" | quote }} + # SSL_CERTFILE environment variable (CA certificate) + {{- if $.Values.config.persistence.database.sql.tls.caFile }} + - name: SSL_CERTFILE + value: {{ $.Values.config.persistence.database.sql.tls.caFile | quote }} + {{- else if $.Values.config.persistence.database.sql.tls.caFiles }} + - name: SSL_CERTFILE + value: {{ index $.Values.config.persistence.database.sql.tls.caFiles 0 | quote }} + {{- end }} + # Client certificate for mutual TLS + {{- if $.Values.config.persistence.database.sql.tls.certFile }} + - name: SSL_CLIENT_CERT + value: {{ $.Values.config.persistence.database.sql.tls.certFile | quote }} + {{- end }} + # Client private key for mutual TLS + {{- if $.Values.config.persistence.database.sql.tls.keyFile }} + - name: SSL_CLIENT_KEY + value: {{ $.Values.config.persistence.database.sql.tls.keyFile | quote }} + {{- end }} + {{- end }} + {{- else if eq $dbDriver "mysql" }} + image: {{ $.Values.schema.checkSchema.mysql.image.repository }}:{{ $.Values.schema.checkSchema.mysql.image.tag }} + imagePullPolicy: {{ $.Values.schema.checkSchema.mysql.image.pullPolicy }} + command: + - sh + - -c + - | + # Build connection string based on TLS configuration + build_mysql_cmd() { + local cmd="mariadb -h $DB_HOST -P $DB_PORT -u $DB_USER" + + # Add SSL parameters if TLS is enabled + if [ "$TLS_ENABLED" = "true" ]; then + case "$SSL_MODE" in + "disable"|"false") + cmd="$cmd --skip-ssl" + ;; + "preferred") + ;; + "required"|"true"|"skip-verify") + cmd="$cmd --ssl --ssl-verify-server-cert=false" + ;; + "verify-ca") + cmd="$cmd --ssl --ssl-verify-server-cert" + ;; + "verify-identity") + cmd="$cmd --ssl --ssl-verify-server-cert" + ;; + *) + cmd="$cmd --ssl" + ;; + esac + + # Add SSL certificate parameters if provided + if [ -n "$SSL_CERTFILE" ]; then + cmd="$cmd --ssl-ca=$SSL_CERTFILE" + fi + + if [ -n "$SSL_CLIENT_CERT" ]; then + cmd="$cmd --ssl-cert=$SSL_CLIENT_CERT" + fi + + if [ -n "$SSL_CLIENT_KEY" ]; then + cmd="$cmd --ssl-key=$SSL_CLIENT_KEY" + fi + fi + + echo "$cmd" + } + + # Wait for MySQL to be ready + echo "Waiting for MySQL to be ready..." + until mariadb-admin ping -h $DB_HOST -P $DB_PORT -u $DB_USER --password=$MYSQL_PWD --skip-ssl --silent; do + echo 'MySQL is not ready yet...' + sleep 5 + done + echo "MySQL is ready!" + + # Check schema versions in both databases + echo "Checking schema versions..." + until + # Check main database schema version + $(build_mysql_cmd) -D $DB_NAME -e " + SELECT curr_version FROM schema_version WHERE db_name = '$DB_NAME';" | grep -q "$DEFAULT_VERSION" && + + # Check visibility database schema version (only if ES is not enabled) + { + if [ "$ES_ENABLED" = "false" ]; then + $(build_mysql_cmd) -D $DB_VISIBILITY_NAME -e " + SELECT curr_version FROM schema_version WHERE db_name = '$DB_VISIBILITY_NAME';" | grep -q "$VISIBILITY_VERSION" + else + true + fi + } + do + echo 'Waiting for MySQL schema to be ready...' + sleep 10 + done + + echo "MySQL schema is ready!" + env: + # Schema version parameters + - name: DEFAULT_VERSION + value: {{ $.Values.schema.version.default | quote }} + - name: VISIBILITY_VERSION + value: {{ $.Values.schema.version.visibility | quote }} + - name: ES_ENABLED + value: {{ $.Values.config.persistence.elasticsearch.enabled | quote }} + # Basic connection parameters + - name: DB_HOST + value: {{ $.Values.config.persistence.database.sql.hosts | quote }} + - name: DB_PORT + value: {{ $.Values.config.persistence.database.mysql.port | default $.Values.config.persistence.database.sql.port | default 3306 | quote }} + - name: DB_NAME + value: {{ $.Values.config.persistence.database.sql.dbname | quote }} + - name: DB_VISIBILITY_NAME + value: {{ $.Values.config.persistence.database.sql.visibilityDbname | quote }} + - name: DB_USER + value: {{ $.Values.config.persistence.database.sql.user | quote }} + # Authentication parameters + - name: MYSQL_PWD + valueFrom: + secretKeyRef: + name: {{ include "cadence.fullname" $ }}-{{ $serviceName }}-secrets + key: MYSQL_PWD + # TLS Configuration + - name: TLS_ENABLED + value: {{ $.Values.config.persistence.database.sql.tls.enabled | quote }} + {{- if $.Values.config.persistence.database.sql.tls.enabled }} + # SSL Mode + - name: SSL_MODE + value: {{ $.Values.config.persistence.database.sql.tls.sslMode | default "require" | quote }} + # SSL_CERTFILE environment variable (CA certificate) + {{- if $.Values.config.persistence.database.sql.tls.caFile }} + - name: SSL_CERTFILE + value: {{ $.Values.config.persistence.database.sql.tls.caFile | quote }} + {{- else if $.Values.config.persistence.database.sql.tls.caFiles }} + - name: SSL_CERTFILE + value: {{ index $.Values.config.persistence.database.sql.tls.caFiles 0 | quote }} + {{- end }} + # Client certificate for mutual TLS + {{- if $.Values.config.persistence.database.sql.tls.certFile }} + - name: SSL_CLIENT_CERT + value: {{ $.Values.config.persistence.database.sql.tls.certFile | quote }} + {{- end }} + # Client private key for mutual TLS + {{- if $.Values.config.persistence.database.sql.tls.keyFile }} + - name: SSL_CLIENT_KEY + value: {{ $.Values.config.persistence.database.sql.tls.keyFile | quote }} + {{- end }} + {{- end }} + {{- end }} + {{- if $.Values.config.persistence.elasticsearch.enabled }} + # ElasticSearch Schema Validation Init Container + - name: check-elasticsearch-schema + image: {{ $.Values.schema.checkSchema.elasticsearch.image.repository | default "alpine/curl" }}:{{ $.Values.schema.checkSchema.elasticsearch.image.tag | default "latest" }} + imagePullPolicy: {{ $.Values.schema.checkSchema.elasticsearch.image.pullPolicy | default "IfNotPresent" }} + command: + - sh + - -c + - | + # Elasticsearch Schema Validation Script + # This script validates Elasticsearch connectivity and schema readiness + + echo "Starting Elasticsearch schema validation..." + + # Build Elasticsearch connection parameters + build_es_connection() { + # Determine protocol - allow override from values or default based on TLS + if [ -n "$ES_PROTOCOL" ]; then + PROTOCOL="$ES_PROTOCOL" + elif [ "$TLS_ENABLED" = "true" ]; then + PROTOCOL="https" + else + PROTOCOL="http" + fi + + # Build curl options for TLS + CURL_OPTS="" + if [ "$TLS_ENABLED" = "true" ]; then + # Configure SSL verification based on host verification setting + if [ "$ENABLE_HOST_VERIFICATION" = "false" ]; then + CURL_OPTS="$CURL_OPTS -k" + fi + + # Add CA certificate if provided + if [ -n "$SSL_CA_FILE" ]; then + CURL_OPTS="$CURL_OPTS --cacert $SSL_CA_FILE" + fi + + # Add client certificate for mutual TLS if provided + if [ -n "$SSL_CLIENT_CERT" ] && [ -n "$SSL_CLIENT_KEY" ]; then + CURL_OPTS="$CURL_OPTS --cert $SSL_CLIENT_CERT --key $SSL_CLIENT_KEY" + fi + + # Override server name if specified + if [ -n "$SSL_SERVER_NAME" ]; then + CURL_OPTS="$CURL_OPTS --resolve $SSL_SERVER_NAME:$ES_PORT:$ES_HOST" + fi + + # Additional TLS options + if [ "$REQUIRE_CLIENT_AUTH" = "true" ]; then + # Client auth is required, ensure we have client cert + if [ -z "$SSL_CLIENT_CERT" ] || [ -z "$SSL_CLIENT_KEY" ]; then + echo "Error: Client authentication required but client certificate/key not provided" + exit 1 + fi + fi + fi + + # Add authentication if user/password provided + if [ -n "$ES_USER" ] && [ -n "$ES_PWD" ]; then + CURL_OPTS="$CURL_OPTS -u $ES_USER:$ES_PWD" + fi + + # Set global variables + BASE_URL="$PROTOCOL://$ES_HOST:$ES_PORT" + + echo "Connecting to Elasticsearch at: $BASE_URL" + echo "TLS Enabled: $TLS_ENABLED" + if [ "$TLS_ENABLED" = "true" ]; then + echo "Host Verification: $ENABLE_HOST_VERIFICATION" + echo "Client Auth Required: $REQUIRE_CLIENT_AUTH" + fi + } + + # Wait for Elasticsearch to be ready + echo "Waiting for Elasticsearch to be ready..." + build_es_connection + + # Check Elasticsearch health + until curl $CURL_OPTS -s -f "$BASE_URL/_cluster/health?wait_for_status=yellow&timeout=5s" > /dev/null; do + echo "Elasticsearch is not ready yet..." + sleep 10 + done + + echo "Elasticsearch is ready!" + + # Get cluster info for debugging + echo "Elasticsearch cluster information:" + CLUSTER_INFO=$(curl $CURL_OPTS -s "$BASE_URL/") + if [ $? -eq 0 ]; then + echo "$CLUSTER_INFO" | grep -E '"cluster_name"|"version"|"number"' || echo "Could not parse cluster info" + else + echo "Warning: Could not retrieve cluster information" + fi + + # Check if template exists and validate schema + echo "Checking Elasticsearch schema template..." + TEMPLATE_URL="$BASE_URL/_template/cadence-visibility-template" + + # Wait for template to exist + until curl $CURL_OPTS -s -f "$TEMPLATE_URL" > /dev/null; do + echo "Waiting for Cadence visibility template to be ready..." + sleep 10 + done + echo "✓ Cadence visibility template exists" + + # Validate template structure + TEMPLATE_RESPONSE=$(curl $CURL_OPTS -s "$TEMPLATE_URL") + if echo "$TEMPLATE_RESPONSE" | grep -q "cadence-visibility-template"; then + echo "✓ Template structure is valid" + else + echo "⚠ Warning: Template structure may be invalid" + fi + + # Check if visibility index exists + echo "Checking visibility index..." + INDEX_URL="$BASE_URL/$VISIBILITY_INDEX" + + # Wait for index to exist + until curl $CURL_OPTS -s -f "$INDEX_URL" > /dev/null; do + echo "Waiting for visibility index '$VISIBILITY_INDEX' to be ready..." + sleep 10 + done + echo "✓ Visibility index '$VISIBILITY_INDEX' exists" + + # Wait for index to be healthy + until curl $CURL_OPTS -s -f "$INDEX_URL/_stats" > /dev/null; do + echo "Waiting for visibility index to be healthy..." + sleep 5 + done + + INDEX_STATS=$(curl $CURL_OPTS -s "$INDEX_URL/_stats") + echo "✓ Visibility index is accessible and healthy" + # Extract basic stats + DOC_COUNT=$(echo "$INDEX_STATS" | grep -o '"count":[0-9]*' | head -1 | cut -d':' -f2) + if [ -n "$DOC_COUNT" ]; then + echo " - Document count: $DOC_COUNT" + fi + + # Additional checks for different ES versions + echo "Performing version-specific checks for ES $ES_VERSION..." + case "$ES_VERSION" in + "v6") + # Wait for _doc type mapping (ES6 compatibility) + TYPE_URL="$BASE_URL/$VISIBILITY_INDEX/_mapping/_doc" + until curl $CURL_OPTS -s -f "$TYPE_URL" > /dev/null; do + echo "Waiting for ES6 document type mapping..." + sleep 5 + done + echo "✓ ES6 document type mapping exists" + ;; + "v7"|"v8") + # Wait for mapping without type (ES7/8 style) + MAPPING_URL="$BASE_URL/$VISIBILITY_INDEX/_mapping" + until curl $CURL_OPTS -s -f "$MAPPING_URL" > /dev/null; do + echo "Waiting for ES7/8 index mapping..." + sleep 5 + done + echo "✓ ES7/8 index mapping exists" + ;; + *) + echo "⚠ Warning: Unknown ES version: $ES_VERSION" + ;; + esac + + # Final validation summary + echo "" + echo "=== Elasticsearch Schema Validation Summary ===" + echo "Cluster: Ready ✓" + echo "Template: Ready ✓" + echo "Index: Ready ✓" + echo "Mapping: Ready ✓" + echo "Version: $ES_VERSION" + echo "===============================================" + + echo "Elasticsearch schema validation completed successfully!" + exit 0 + env: + # Basic Elasticsearch connection parameters + - name: ES_HOST + value: {{ $.Values.config.persistence.elasticsearch.hosts | quote }} + - name: ES_PORT + value: {{ $.Values.config.persistence.elasticsearch.port | default 9200 | quote }} + - name: ES_PROTOCOL + value: {{ $.Values.config.persistence.elasticsearch.protocol | default "" | quote }} + - name: VISIBILITY_INDEX + value: {{ $.Values.config.persistence.elasticsearch.visibilityIndex | quote }} + - name: ES_VERSION + value: {{ $.Values.config.persistence.elasticsearch.version | quote }} + # Authentication parameters + {{- if $.Values.config.persistence.elasticsearch.user }} + - name: ES_USER + value: {{ $.Values.config.persistence.elasticsearch.user | quote }} + {{- end }} + {{- if $.Values.config.persistence.elasticsearch.password }} + - name: ES_PWD + valueFrom: + secretKeyRef: + name: {{ include "cadence.fullname" $ }}-{{ $serviceName }}-secrets + key: ES_PWD + {{- end }} + # TLS Configuration + - name: TLS_ENABLED + value: {{ $.Values.config.persistence.elasticsearch.tls.enabled | quote }} + {{- if $.Values.config.persistence.elasticsearch.tls.enabled }} + - name: ENABLE_HOST_VERIFICATION + value: {{ $.Values.config.persistence.elasticsearch.tls.enableHostVerification | quote }} + - name: REQUIRE_CLIENT_AUTH + value: {{ $.Values.config.persistence.elasticsearch.tls.requireClientAuth | quote }} + # CA certificate file + {{- if $.Values.config.persistence.elasticsearch.tls.caFile }} + - name: SSL_CA_FILE + value: {{ $.Values.config.persistence.elasticsearch.tls.caFile | quote }} + {{- else if $.Values.config.persistence.elasticsearch.tls.caFiles }} + - name: SSL_CA_FILE + value: {{ index $.Values.config.persistence.elasticsearch.tls.caFiles 0 | quote }} + {{- end }} + # Client certificate for mutual TLS + {{- if $.Values.config.persistence.elasticsearch.tls.certFile }} + - name: SSL_CLIENT_CERT + value: {{ $.Values.config.persistence.elasticsearch.tls.certFile | quote }} + {{- end }} + # Client private key for mutual TLS + {{- if $.Values.config.persistence.elasticsearch.tls.keyFile }} + - name: SSL_CLIENT_KEY + value: {{ $.Values.config.persistence.elasticsearch.tls.keyFile | quote }} + {{- end }} + # Server name override + {{- if $.Values.config.persistence.elasticsearch.tls.serverName }} + - name: SSL_SERVER_NAME + value: {{ $.Values.config.persistence.elasticsearch.tls.serverName | quote }} + {{- end }} + {{- end }} + {{- end }} + volumeMounts: + {{- with $.Values.global.tls.volumeMounts }} + {{- toYaml . | nindent 8 }} + {{- end }} containers: - name: cadence-{{ $serviceName }} {{- $globalImage := $.Values.global.image | default dict }} @@ -130,29 +753,29 @@ spec: {{- end }} volumeMounts: - name: config - mountPath: /etc/cadence/config/dynamicconfig/config.yaml + mountPath: /etc/cadence/config/config_template.yaml + subPath: config_template.yaml + - name: config + mountPath: {{ $.Values.config.dynamicConfig.filebased.filepath | default "/etc/cadence/config/dynamicconfig/config.yaml" }} subPath: dynamic_config.yaml + {{- with $.Values.global.tls.volumeMounts }} + {{- toYaml . | nindent 8 }} + {{- end }} env: - name: SERVICES value: {{ $serviceName }} - - name: DYNAMIC_CONFIG_FILE_PATH - value: "/etc/cadence/config/dynamicconfig/config.yaml" - - name: PRIMARY_FRONTEND_SERVICE - value: {{ include "cadence.fullname" $ }}-frontend.{{ $.Release.Namespace }}.svc.cluster.local - - name: BROADCAST_ADDRESS + - name: POD_IP valueFrom: fieldRef: fieldPath: status.podIP - - name: CASSANDRA_SEEDS - value: {{ include "cassandra.endpoint" $ }} + {{- if $.Values.config.log.useEnvVars }} {{- $globalLog := $.Values.global.log | default dict }} {{- $serviceLog := $service.log | default $globalLog }} - name: LOG_LEVEL value: {{ $serviceLog.level | default "info" | quote }} - name: LOG_STDOUT value: {{ $serviceLog.stdout | default true | quote }} - - name: RINGPOP_SEEDS - value: {{ include "cadence.ringpopSeeds" $ }} + {{- end }} {{- $globalEnv := $.Values.global.env | default list }} {{- $serviceEnv := $service.env | default list }} {{- $mergedEnv := concat $globalEnv $serviceEnv }} @@ -161,7 +784,10 @@ spec: {{- end }} {{- $globalSecrets := $.Values.global.secretEnv | default list }} {{- $serviceSecrets := $service.secretEnv | default list }} - {{- $mergedSecrets := concat $globalSecrets $serviceSecrets }} + {{- $userSecrets := concat $globalSecrets $serviceSecrets -}} + {{- include "cadence.databaseSecrets" $ -}} + {{- $databaseSecrets := $.databaseSecrets | default list -}} + {{- $mergedSecrets := concat $userSecrets $databaseSecrets -}} {{- if $mergedSecrets }} envFrom: - secretRef: @@ -175,4 +801,7 @@ spec: - name: config configMap: name: {{ include "cadence.fullname" $ }}-configmap -{{- end }} + {{- with $.Values.global.tls.volumes }} + {{- toYaml . | nindent 6 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/cadence/templates/server-secret.yaml b/charts/cadence/templates/server-secret.yaml index 3073731..c8033e9 100644 --- a/charts/cadence/templates/server-secret.yaml +++ b/charts/cadence/templates/server-secret.yaml @@ -1,17 +1,21 @@ {{/* This template creates secrets for all Cadence Server services (frontend, history, matching, worker) -It only creates secrets if secretEnv is defined for global or individual services +It creates secrets from secretEnv configuration and automatically adds database/service passwords */}} {{- $services := list "frontend" "history" "matching" "worker" -}} -{{- $hasGlobalSecrets := .Values.global.secretEnv -}} {{- range $serviceName := $services -}} {{- $service := index $.Values $serviceName -}} -{{- $hasServiceSecrets := $service.secretEnv -}} {{- $globalSecrets := $.Values.global.secretEnv | default list -}} {{- $serviceSecrets := $service.secretEnv | default list -}} -{{- $mergedSecrets := concat $globalSecrets $serviceSecrets -}} +{{- $userSecrets := concat $globalSecrets $serviceSecrets -}} + +{{- /* Call helper to populate database secrets */ -}} +{{- include "cadence.databaseSecrets" $ -}} +{{- $databaseSecrets := $.databaseSecrets | default list -}} + +{{- $mergedSecrets := concat $userSecrets $databaseSecrets -}} {{- if $mergedSecrets }} --- diff --git a/charts/cadence/values.yaml b/charts/cadence/values.yaml index 21b9fcc..3c8ff39 100644 --- a/charts/cadence/values.yaml +++ b/charts/cadence/values.yaml @@ -8,7 +8,7 @@ global: # -- Global image configuration (shared only by Cadence Server services [frontend, worker, matching and history]) image: repository: "docker.io/ubercadence/server" - tag: "v1.3.1-auto-setup" + tag: "v1.3.1" pullPolicy: IfNotPresent # -- Image pull secrets for private registries @@ -16,21 +16,23 @@ global: # - name: myregistrykey # -- Global environment variables (shared only by Cadence Server services [frontend, worker, matching and history]) - env: - - name: ENABLE_ES - value: "false" - - name: SKIP_SCHEMA_SETUP - value: "true" - - name: RINGPOP_BOOTSTRAP_MODE - value: "dns" - - name: BIND_ON_IP - value: 0.0.0.0 + env: [] + # - name: ENV_VAR + # value: "value" + # Enable this variable if you are using Google Storage. + # - name: GOOGLE_APPLICATION_CREDENTIALS + # value: "absolute/path/to/json/file" # -- Global secret environment variables (shared only by Cadence Server services [frontend, worker, matching and history]) secretEnv: [] # - name: GLOBAL_SECRET_ENV_VAR # value: "value" - + # Enable this variables if you are using S3. + # - name: AWS_ACCESS_KEY_ID + # value: "value" + # - name: AWS_SECRET_ACCESS_KEY + # value: "value" + # -- Global pod security context podSecurityContext: {} # runAsNonRoot: true @@ -57,6 +59,38 @@ global: # -- Global node selector nodeSelector: {} + # -- Global TLS volumes configuration + tls: + # -- Additional volumes for TLS certificates (The mode is important to have the minimum permissions) + volumes: [] + # - name: multiple-ca-certs + # configMap: + # name: cassandra-ca-bundle + # items: + # - key: root-ca.crt + # path: root-ca.pem + # - key: intermediate-ca.crt + # path: intermediate-ca.pem + # - name: example-tls-certs + # secret: + # secretName: example-crt-secret + # items: + # - key: ca.crt + # path: ca.pem + # mode: 0644 + # - key: tls.crt + # path: client.pem + # mode: 0644 + # - key: tls.key + # path: client-key.pem + # mode: 0600 + + # -- Volume mounts for TLS certificates + volumeMounts: [] + # - name: example-tls-certs + # mountPath: /etc/cadence/ssl/ + # readOnly: true + # -- Global logging configuration (shared only by Cadence Server services [frontend, worker, matching and history]) log: # -- Enable stdout logging @@ -412,7 +446,7 @@ web: # -- Image configuration for Web UI image: repository: "docker.io/ubercadence/web" - tag: "v4.0.3" + tag: "v4.0.5" pullPolicy: IfNotPresent imagePullSecrets: [] # - name: myregistrykey @@ -518,14 +552,6 @@ web: # hosts: # - cadence.example.com -# Dynamic configuration -dynamicConfig: - # -- Dynamic config values to be set in the Cadence server - # List of keys can be found at https://pkg.go.dev/github.com/uber/cadence@v1.3.0/common/dynamicconfig/dynamicproperties - values: - history.workflowIDExternalRateLimitEnabled: - - value: true - # Service Account configuration serviceAccount: # -- Enable service account creation @@ -632,21 +658,505 @@ autoscaling: targetCPUUtilizationPercentage: 70 targetMemoryUtilizationPercentage: 80 -############################## -### DATABASE CONFIGURATION ### -############################## +############################################# +########### CADENCE CONFIGURATION ########### +############################################# +config: + # Log configuration + log: + # -- Enable stdout logging (inherits from global.log if not specified) + stdout: ~ + # -- Logging level: debug, info, warn, error (inherits from global.log if not specified) + level: ~ + # -- Output file path for logging (if stdout is false) + outputFile: "" + # -- Log level key name (defaults to "level") + levelKey: level + # -- Log encoding format: json, console (defaults to "json") + encoding: json + # -- Allow using environment variables for log configuration. If enabled, it will use ENV variable of each server component. + useEnvVars: false + + # Ringpop configuration for service discovery + ringpop: + # -- Ringpop cluster name + name: "cadence" + # -- Bootstrap mode: dns, hosts, file + bootstrapMode: "dns" + # -- Maximum duration to wait for joining the ring + maxJoinDuration: "30s" + + # Persistence configuration + persistence: + # -- Number of history shards for partitioning (CANNOT BE CHANGED ONCE SET) + numHistoryShards: 4 + # -- Name of the default datastore + defaultStore: "default" + # -- Name of the visibility datastore (basic visibility) + visibilityStore: "visibility" + # -- Name of the advanced visibility datastore + advancedVisibilityStore: "es-visibility" + # -- Enable persistence latency histogram metrics + enablePersistenceLatencyHistogramMetrics: false + + # Database configuration + database: + # -- Database driver: cassandra, mysql, postgres + driver: "cassandra" + + # Common SQL configuration (applies to both MySQL and PostgreSQL) + sql: + # -- Database host. Can reference Kubernetes services + hosts: "mysql-service.mysql-namespace.svc.cluster.local" + # -- Database port (will use driver default if not specified) + port: null + # -- Database name for main data + dbname: "cadence" + # -- Database name for visibility data + visibilityDbname: "cadence_visibility" + # -- Database username + user: "cadence" + # -- Database password + password: "" + # -- Maximum number of connections + maxConns: 20 + # -- Maximum number of idle connections + maxIdleConns: 20 + # -- Maximum connection lifetime + maxConnLifetime: "1h" + # -- Number of database shards (default: 1) + numShards: 1 + # -- Encoding type for SQL blobs + encodingType: "thriftrw" + # -- Decoding types for SQL blobs + decodingTypes: ["thriftrw"] + # -- Use multiple databases for sharding + useMultipleDatabases: false + # -- Multiple databases configuration (when useMultipleDatabases is true) + multipleDatabasesConfig: [] + # -- Connection attributes (key-value pairs for connection string) + connectAttributes: {} + # TLS configuration for SQL databases + tls: + # -- Enable TLS + enabled: false + # -- SSL mode (for PostgreSQL: disable, allow, prefer, require, verify-ca, verify-full) + # -- For MySQL: false, true, skip-verify, preferred. (Additional this should work: required, verify-ca, verify-identity) + sslMode: "" + # -- Path to CA certificate file + caFile: "" + # -- Multiple CA certificate files + caFiles: [] + # -- Path to client certificate file + certFile: "" + # -- Path to client private key file + keyFile: "" + # -- Enable hostname verification (inverse of skipHostVerification) + enableHostVerification: true + # -- Require client authentication for mutual TLS + requireClientAuth: false + # -- Server name for certificate verification + serverName: "" + + # MySQL-specific configuration + mysql: + # -- Default port for MySQL (overrides sql.port if specified) + port: 3306 + # -- Enable transaction isolation compatibility mode + txIsolationCompat: false + + # PostgreSQL-specific configuration + postgres: + # -- Default port for PostgreSQL (overrides sql.port if specified) + port: 5432 + + # Cassandra configuration + cassandra: + # -- Cassandra hosts. Can reference Kubernetes services + hosts: "cassandra-service.cadence.svc.cluster.local" + # -- Cassandra port + port: 9042 + # -- Cassandra keyspace for main data + keyspace: "cadence" + # -- Cassandra keyspace for visibility data + visibilityKeyspace: "cadence_visibility" + # -- Cassandra username + user: "cassandra" + # -- Cassandra password + password: "cassandra" + # -- Cassandra protocol version + protoVersion: 4 + # -- AWS region filter for Cassandra (if using AWS Keyspaces) + region: "" + # -- Datacenter filter for Cassandra + datacenter: "" + # -- Maximum number of connections + maxConns: 10 + # -- Connection timeout + connectTimeout: "10s" + # -- Query timeout + timeout: "1s" + # -- Default consistency level + consistency: "LOCAL_QUORUM" + # -- Serial consistency level + serialConsistency: "LOCAL_SERIAL" + # -- Host selection policy + hostSelectionPolicy: "tokenaware,roundrobin" + # -- Allowed authenticators for custom authentication + allowedAuthenticators: [] + # -- Additional connection attributes + connectAttributes: {} + # TLS configuration for Cassandra + tls: + # -- Enable TLS + enabled: false + # -- CA certificate file to verify server certificates + caFile: "" + # -- Multiple CA certificate files (alternative to caFile) + caFiles: [] + # -- Client certificate file for mutual TLS + certFile: "" + # -- Client private key file for mutual TLS + keyFile: "" + # -- Verify server hostname matches certificate + enableHostVerification: true + # -- Require client certificate authentication + requireClientAuth: false + # -- Override server name for certificate verification + serverName: "cassandra" + + # Elasticsearch configuration for advanced visibility + elasticsearch: + # -- Enable Elasticsearch for advanced visibility + enabled: false + # -- Elasticsearch version (v6, use v7 for v7 or higher) + version: "v7" + # -- Elasticsearch username + user: "" + # -- Elasticsearch password + password: "" + # -- Protocol to use (http/https). If not specified, auto-detected based on TLS settings + protocol: "" + # -- Elasticsearch host. + hosts: "elasticsearch-service.elastic-namespace.svc.cluster.local" + # -- Elasticsearch port + port: 9200 + # -- Elasticsearch visibility index name + visibilityIndex: "cadence-visibility" + # -- Enable AWS signing (for AWS Elasticsearch) + awsSigning: + enabled: false + region: "" + service: "es" + # TLS configuration for ElasticSearch + tls: + # -- Enable TLS + enabled: false + # -- CA certificate file to verify server certificates + caFile: "" + # -- Multiple CA certificate files (alternative to caFile) + caFiles: [] + # -- Client certificate file for mutual TLS + certFile: "" + # -- Client private key file for mutual TLS + keyFile: "" + # -- Verify server hostname matches certificate + enableHostVerification: true + # -- Require client certificate authentication + requireClientAuth: false + # -- Override server name for certificate verification + serverName: "" + + # Cluster configuration + cluster: + # -- Version increment used during cluster failover operations + failoverVersionIncrement: 10 + # -- Name of the primary cluster in a multi-cluster setup + primaryClusterName: "cluster0" + # -- Name of the current cluster + currentClusterName: "cluster0" + # -- Whether this cluster is not the primary cluster + isNotPrimary: false + # -- RPC transport protocol (grpc or tchannel) + rpcTransport: "grpc" + # -- Initial failover version for this cluster + initialFailoverVersion: 0 + # -- Cluster group configuration with additional clusters + clusterGroup: + # Example of additional cluster configuration: + # cluster1: + # enabled: true + # initialFailoverVersion: 0 + # rpcAddress: "cadence-cluster1.example.com:7933" + # rpcTransport: "grpc" + # -- Cluster redirection policy for cross-cluster operations + clusterRedirectionPolicy: + # -- Policy for handling cross-cluster requests (noop, selected-apis-forwarding, all-domain-apis-forwarding, selected-apis-forwarding-v2) + policy: "noop" + + # Services configuration + services: + # -- gRPC max message size + grpcMaxMsgSize: 4194304 # 4MB + + # Metrics configuration + metrics: + # -- Metrics type: statsd, prometheus + type: "prometheus" + # StatsD configuration + statsd: + # -- StatsD endpoint. Can reference Kubernetes services + endpoint: "" + # -- Metric prefixes for each service + prefixes: + frontend: "cadence-frontend" + matching: "cadence-matching" + history: "cadence-history" + worker: "cadence-worker" + # Prometheus configuration + prometheus: + # -- Timer type: histogram, summary + timerType: "histogram" + + # Pprof configuration + pprof: + # -- Enable pprof endpoints + enabled: false + # -- Pprof ports for each service + ports: + frontend: 6060 + matching: 6061 + history: 6062 + worker: 6063 + + # Kafka configuration for async workflows + kafka: + # -- Enable Kafka for async workflows + enabled: false + # -- Kafka broker service. Can reference Kubernetes services + brokers: "kafka-service.kafka-namespace.svc.cluster.local" + # -- Kafka port + port: 9092 + # -- Kafka visibility topic name + visibilityTopic: "cadence-visibility" + # -- Kafka visibility DLQ topic name + visibilityDLQTopic: "cadence-visibility-dlq" + # -- Topic properties (optional) + topicProperties: {} + # TLS configuration for Kafka + tls: + # -- Enable TLS + enabled: false + # -- CA certificate file to verify server certificates + caFile: "" + # -- Multiple CA certificate files (alternative to caFile) + caFiles: [] + # -- Client certificate file for mutual TLS + certFile: "" + # -- Client private key file for mutual TLS + keyFile: "" + # -- Verify server hostname matches certificate + enableHostVerification: true + # -- Require client certificate authentication + requireClientAuth: false + # -- Override server name for certificate verification + serverName: "" + # SASL configuration + sasl: + # -- Enable SASL authentication + enabled: false + # -- SASL mechanism: plain, sha512 or sha256 + mechanism: "PLAIN" + # -- SASL username + username: "" + # -- SASL password + password: "" + + # Archival configuration + archival: + # History archival configuration + history: + # -- Archival status: enabled, disabled, paused + status: "disabled" + # -- Enable reading from archives + enableRead: false + # -- Archive providers configuration + provider: + # -- Storage type: filestore, s3, gcs + type: "filestore" + # Filestore archiver + filestore: + # -- File mode for archived files + fileMode: "0644" + # -- Directory mode for archive directories + dirMode: "0755" + # S3 archiver + # - Documentation for S3 here: https://github.com/cadence-workflow/cadence/blob/v1.3.0/common/archiver/s3store/README.md + s3store: + # -- AWS region + region: "" + # -- S3 endpoint (for S3-compatible storage) + endpoint: "" + # -- Force path style URLs + s3ForcePathStyle: false + # Google Cloud Storage archiver + # - Documentation for GStorage here: https://github.com/cadence-workflow/cadence/blob/v1.3.0/common/archiver/gcloud/README.md + gstorage: + # -- Path to service account key file + credentialsPath: "" + + # Visibility archival configuration + visibility: + # -- Archival status: enabled, disabled, paused + status: "disabled" + # -- Enable reading from archives + enableRead: false + # -- Archive providers configuration + provider: + # -- Storage type: filestore, s3, gcs + type: "filestore" + # Filestore archiver + filestore: + # -- File mode for archived files + fileMode: "0644" + # -- Directory mode for archive directories + dirMode: "0755" + # S3 archiver + # - Documentation for S3 here: https://github.com/cadence-workflow/cadence/blob/v1.3.0/common/archiver/s3store/README.md + s3store: + # -- AWS region + region: "" + # -- S3 endpoint (for S3-compatible storage) + endpoint: "" + # -- Force path style URLs + s3ForcePathStyle: false + # Google Cloud Storage archiver + # - Documentation for GStorage here: https://github.com/cadence-workflow/cadence/blob/v1.3.0/common/archiver/gcloud/README.md + gstorage: + # -- Path to service account key file + credentialsPath: "" + + # Domain defaults configuration + domainDefaults: + # -- Default archival settings for new domains + # - Documentation for S3 here: https://github.com/cadence-workflow/cadence/blob/v1.3.0/common/archiver/s3store/README.md + # - Documentation for GStorage here: https://github.com/cadence-workflow/cadence/blob/v1.3.0/common/archiver/gcloud/README.md + archival: + history: + # -- Default history archival status: enabled, disabled + status: "disabled" + # -- Default history archival URI + URI: "" + visibility: + # -- Default visibility archival status: enabled, disabled + status: "disabled" + # -- Default visibility archival URI + URI: "" + + # Blobstore configuration + blobstore: + # Filestore blobstore + filestore: + # -- Output directory for blob storage + outputDirectory: "/etc/cadence/blobstore" + + # Public client configuration + publicClient: + # -- Frontend service address (defaults to current cluster's RPC address) + hostPort: "" + # -- Transport protocol: grpc, tchannel + transport: "grpc" + # -- DNS refresh interval + refreshInterval: "10s" + + # Dynamic configuration + dynamicConfig: + # -- Dynamic config client type: noop, filebased, configstore + client: "filebased" + # File-based dynamic config + filebased: + # -- Path to dynamic config file + filepath: "/etc/cadence/config/dynamicconfig/config.yaml" + # -- Poll interval for config changes + pollInterval: "60s" + + # Async workflow queues configuration + # This is not operative yet. It will be in next releases. + asyncWorkflowQueues: + # -- Enable async workflow queues + enabled: false + # -- Async workflow queue providers + default-queue: + # -- Queue type: kafka + type: "kafka" + # -- Queue configuration + config: + # -- Kafka topic for async workflows + topic: "cadence-async-wf" + # -- Kafka cluster reference + cluster: "default" + +# Dynamic configuration +dynamicConfig: + # -- Dynamic config values to be set in the Cadence server + # List of keys can be found at https://pkg.go.dev/github.com/uber/cadence/common/dynamicconfig/dynamicproperties + values: + system.minRetentionDays: + - value: 0 + constraints: {} + # Visibility write have to be set in funtion of advanced visibility or not. See https://pkg.go.dev/github.com/uber/cadence/common/dynamicconfig/dynamicproperties#WriteVisibilityStoreName for more information. + system.writeVisibilityStoreName: + - value: "db" + # Visibility read have to be set in funtion of advanced visibility or not. See https://pkg.go.dev/github.com/uber/cadence/common/dynamicconfig/dynamicproperties#ReadVisibilityStoreName for more information. + system.readVisibilityStoreName: + - value: "db" + +############################################################### +#################### SCHEMA CONFIGURATION ##################### +############################################################### +schema: + version: + default: "0.42" + visibility: "0.9" + # createJob: + # enabled: false + # No support for mysql & postgresql yet. + setupJob: + enabled: true + # updateJob: + # enabled: false + checkSchema: + cassandra: + image: + repository: "cassandra" + tag: "4.0" + pullPolicy: IfNotPresent + mysql: + image: + repository: "alpine/mysql" + tag: "latest" + pullPolicy: IfNotPresent + postgres: + image: + repository: "alpine/psql" + tag: "latest" + pullPolicy: IfNotPresent + elasticsearch: + image: + repository: alpine/curl + tag: "latest" + pullPolicy: IfNotPresent + +############################################################### +################### DATABASE CONFIGURATION #################### +# Use only if you want to deploy the database from this chart # +############################################################### # Cassandra configuration cassandra: # -- External Cassandra endpoint to connect to. Required when cassandra.deployment.enabled is set to false endpoint: "" - schema: - # -- Cassandra schema version of the Cadence keyspace to use - version: "0.42" - # -- Cassandra schema version of the Cadence visibility keyspace to use - visibility_version: "0.9" - deployment: # -- When enabled, a single instance Cassandra will be deployed as part of the Helm chart # -- When disabled, the Cassandra deployment is expected to be provided externally From 55e7237dc6d678133e255c5d633b02f4bcc74b0d Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed, 25 Jun 2025 12:26:08 +0200 Subject: [PATCH 02/37] Create schema-create-job.yaml --- .../cadence/templates/schema-create-job.yaml | 407 ++++++++++++++++++ 1 file changed, 407 insertions(+) create mode 100644 charts/cadence/templates/schema-create-job.yaml diff --git a/charts/cadence/templates/schema-create-job.yaml b/charts/cadence/templates/schema-create-job.yaml new file mode 100644 index 0000000..cfa23d2 --- /dev/null +++ b/charts/cadence/templates/schema-create-job.yaml @@ -0,0 +1,407 @@ +{{- if .Values.schema.createJob.enabled -}} +{{- $dbDriver := .Values.config.persistence.database.driver }} +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "cadence.fullname" . }}-schema-create + labels: + {{- include "cadence.labels" . | nindent 4 }} + app.kubernetes.io/component: schema-create + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "-3" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded +spec: + template: + metadata: + labels: + {{- include "cadence.labels" . | nindent 8 }} + app.kubernetes.io/component: schema-setup + spec: + {{- if $.Values.serviceAccount.create }} + serviceAccountName: {{ include "cadence.serviceAccountName" $ }} + {{- end }} + {{- $globalImagePullSecrets := $.Values.global.imagePullSecrets | default list }} + {{- $schemaImagePullSecrets := $.Values.frontend.imagePullSecrets | default list }} + {{- $mergedImagePullSecrets := concat $globalImagePullSecrets $schemaImagePullSecrets }} + {{- if $mergedImagePullSecrets }} + imagePullSecrets: + {{- toYaml $mergedImagePullSecrets | nindent 8 }} + {{- end }} + {{- $globalNodeSelector := $.Values.global.nodeSelector | default dict }} + {{- $schemaNodeSelector := $.Values.schema.nodeSelector | default $globalNodeSelector }} + {{- if $schemaNodeSelector }} + nodeSelector: + {{- toYaml $schemaNodeSelector | nindent 8 }} + {{- end }} + {{- $globalAffinity := $.Values.global.affinity | default dict }} + {{- $schemaAffinity := $.Values.schema.affinity | default $globalAffinity }} + {{- if $schemaAffinity }} + affinity: + {{- toYaml $schemaAffinity | nindent 8 }} + {{- end }} + {{- $globalTolerations := $.Values.global.tolerations | default list }} + {{- $schemaTolerations := $.Values.schema.tolerations | default $globalTolerations }} + {{- if $schemaTolerations }} + tolerations: + {{- toYaml $schemaTolerations | nindent 8 }} + {{- end }} + restartPolicy: OnFailure + containers: + - name: cadence-schema-create + {{- $globalImage := .Values.global.image | default dict }} + {{- $schemaImage := $.Values.frontend.image | default dict }} + {{- $repository := $schemaImage.repository | default $globalImage.repository }} + {{- $tag := $schemaImage.tag | default $globalImage.tag }} + image: {{ $repository }}:{{ $tag }} + {{- $pullPolicy := $schemaImage.pullPolicy | default $globalImage.pullPolicy | default "IfNotPresent" }} + imagePullPolicy: {{ $pullPolicy }} + {{- if eq $dbDriver "cassandra" }} + # Cassandra Schema Setup + command: + - sh + - -c + - | + set -e + echo "Starting Cadence schema setup for driver: $DB_DRIVER" + echo "=== Setting up Cassandra Schema ===" + + # Build cassandra-tool command with TLS options + build_cassandra_cmd() { + local cmd="cadence-cassandra-tool --ep $DB_HOST" + + # Add authentication + if [ -n "$DB_USER" ]; then + cmd="$cmd -u $DB_USER" + fi + if [ -n "$CASSANDRA_PASSWORD" ]; then + cmd="$cmd -pw $CASSANDRA_PASSWORD" + fi + + # Add allowed authenticators from environment variable + if [ -n "$ALLOWED_AUTHENTICATORS" ]; then + cmd="$cmd $ALLOWED_AUTHENTICATORS" + fi + + # Add TLS options if enabled + if [ "$TLS_ENABLED" = "true" ]; then + cmd="$cmd --tls" + if [ -n "$SSL_CERTFILE" ]; then + cmd="$cmd --tls-ca-file $SSL_CERTFILE" + fi + if [ -n "$SSL_CLIENT_CERT" ]; then + cmd="$cmd --tls-cert-file $SSL_CLIENT_CERT" + fi + if [ -n "$SSL_CLIENT_KEY" ]; then + cmd="$cmd --tls-key-file $SSL_CLIENT_KEY" + fi + fi + + echo "$cmd" + } + + # Setup main database schema + echo "Creating main keyspace: $DB_NAME" + $(build_cassandra_cmd) create -k $DB_NAME --rf $REPLICATION_FACTOR + + echo "Setting up main schema version 0.0" + $(build_cassandra_cmd) -k $DB_NAME setup-schema -v 0.0 + + echo "Updating main schema to latest version" + $(build_cassandra_cmd) -k $DB_NAME update-schema -d $CADENCE_HOME/schema/cassandra/cadence/versioned + + # Setup visibility database schema (only if ES is not enabled) + if [ "$ES_ENABLED" = "false" ]; then + echo "Creating visibility keyspace: $DB_VISIBILITY_NAME" + $(build_cassandra_cmd) create -k $DB_VISIBILITY_NAME --rf $REPLICATION_FACTOR + + echo "Setting up visibility schema version 0.0" + $(build_cassandra_cmd) -k $DB_VISIBILITY_NAME setup-schema -v 0.0 + + echo "Updating visibility schema to latest version" + $(build_cassandra_cmd) -k $DB_VISIBILITY_NAME update-schema -d $CADENCE_HOME/schema/cassandra/visibility/versioned + else + echo "Skipping visibility schema setup (Elasticsearch enabled)" + fi + + echo "Schema setup completed successfully!" + env: + # Common environment variables + - name: DB_DRIVER + value: {{ $dbDriver | quote }} + - name: CADENCE_HOME + value: "/etc/cadence" + - name: ES_ENABLED + value: {{ .Values.config.persistence.elasticsearch.enabled | quote }} + # Cassandra specific environment variables + - name: DB_HOST + value: {{ .Values.config.persistence.database.cassandra.hosts | quote }} + - name: DB_NAME + value: {{ .Values.config.persistence.database.cassandra.keyspace | quote }} + - name: DB_VISIBILITY_NAME + value: {{ .Values.config.persistence.database.cassandra.visibilityKeyspace | quote }} + - name: REPLICATION_FACTOR + value: {{ .Values.config.persistence.database.cassandra.replicationFactor | default 1 | quote }} + # Allowed authenticators (build --aa parameters) + - name: ALLOWED_AUTHENTICATORS + value: {{ if .Values.config.persistence.database.cassandra.allowedAuthenticators }}{{ range $index, $auth := .Values.config.persistence.database.cassandra.allowedAuthenticators }}{{ if $index }} {{ end }}--aa {{ $auth | quote }}{{ end }}{{ else }}"--aa org.apache.cassandra.auth.PasswordAuthenticator"{{ end }} + # Authentication parameters (conditional) + {{- if .Values.config.persistence.database.cassandra.user }} + - name: DB_USER + value: {{ .Values.config.persistence.database.cassandra.user | quote }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.password }} + - name: CASSANDRA_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "cadence.fullname" $ }}-frontend-secrets + key: CASSANDRA_PASSWORD + {{- end }} + # TLS Configuration + - name: TLS_ENABLED + value: {{ .Values.config.persistence.database.cassandra.tls.enabled | quote }} + {{- if .Values.config.persistence.database.cassandra.tls.enabled }} + # SSL_CERTFILE environment variable (CA certificate) + {{- if .Values.config.persistence.database.cassandra.tls.caFile }} + - name: SSL_CERTFILE + value: {{ .Values.config.persistence.database.cassandra.tls.caFile | quote }} + {{- else if .Values.config.persistence.database.cassandra.tls.caFiles }} + - name: SSL_CERTFILE + value: {{ index .Values.config.persistence.database.cassandra.tls.caFiles 0 | quote }} + {{- end }} + # Client certificate for mutual TLS + {{- if .Values.config.persistence.database.cassandra.tls.certFile }} + - name: SSL_CLIENT_CERT + value: {{ .Values.config.persistence.database.cassandra.tls.certFile | quote }} + {{- end }} + # Client private key for mutual TLS + {{- if .Values.config.persistence.database.cassandra.tls.keyFile }} + - name: SSL_CLIENT_KEY + value: {{ .Values.config.persistence.database.cassandra.tls.keyFile | quote }} + {{- end }} + {{- end }} + + {{- else if eq $dbDriver "postgres" }} + # PostgreSQL Schema Setup + command: + - sh + - -c + - | + set -e + echo "Starting Cadence schema setup for driver: $DB_DRIVER" + echo "=== Setting up PostgreSQL Schema ===" + + # Build sql-tool command with TLS options + build_postgres_cmd() { + local cmd="cadence-sql-tool --ep $DB_HOST -p $DB_PORT -u $DB_USER -pw $POSTGRES_PWD --plugin postgres" + + # Add TLS options if enabled + if [ "$TLS_ENABLED" = "true" ]; then + cmd="$cmd --tls" + if [ -n "$SSL_CERTFILE" ]; then + cmd="$cmd --tls-ca-file $SSL_CERTFILE" + fi + if [ -n "$SSL_CLIENT_CERT" ]; then + cmd="$cmd --tls-cert-file $SSL_CLIENT_CERT" + fi + if [ -n "$SSL_CLIENT_KEY" ]; then + cmd="$cmd --tls-key-file $SSL_CLIENT_KEY" + fi + fi + + echo "$cmd" + } + + # Create main database + echo "Creating main database: $DB_NAME" + $(build_postgres_cmd) create-database --db $DB_NAME + + echo "Setting up main schema version 0.0" + $(build_postgres_cmd) --db $DB_NAME setup-schema -v 0.0 + + echo "Updating main schema to latest version" + $(build_postgres_cmd) --db $DB_NAME update-schema -d $CADENCE_HOME/schema/postgres/cadence/versioned + + # Setup visibility database (only if ES is not enabled) + if [ "$ES_ENABLED" = "false" ]; then + echo "Creating visibility database: $DB_VISIBILITY_NAME" + $(build_postgres_cmd) create-database --db $DB_VISIBILITY_NAME + + echo "Setting up visibility schema version 0.0" + $(build_postgres_cmd) --db $DB_VISIBILITY_NAME setup-schema -v 0.0 + + echo "Updating visibility schema to latest version" + $(build_postgres_cmd) --db $DB_VISIBILITY_NAME update-schema -d $CADENCE_HOME/schema/postgres/visibility/versioned + else + echo "Skipping visibility schema setup (Elasticsearch enabled)" + fi + + echo "Schema setup completed successfully!" + env: + # Common environment variables + - name: DB_DRIVER + value: {{ $dbDriver | quote }} + - name: CADENCE_HOME + value: "/etc/cadence" + - name: ES_ENABLED + value: {{ .Values.config.persistence.elasticsearch.enabled | quote }} + # PostgreSQL specific environment variables + - name: DB_HOST + value: {{ .Values.config.persistence.database.sql.hosts | quote }} + - name: DB_PORT + value: {{ .Values.config.persistence.database.postgres.port | default .Values.config.persistence.database.sql.port | default 5432 | quote }} + - name: DB_NAME + value: {{ .Values.config.persistence.database.sql.dbname | quote }} + - name: DB_VISIBILITY_NAME + value: {{ .Values.config.persistence.database.sql.visibilityDbname | quote }} + - name: DB_USER + value: {{ .Values.config.persistence.database.sql.user | quote }} + # Authentication parameters + - name: POSTGRES_PWD + valueFrom: + secretKeyRef: + name: {{ include "cadence.fullname" $ }}-frontend-secrets + key: POSTGRES_PWD + # TLS Configuration + - name: TLS_ENABLED + value: {{ .Values.config.persistence.database.sql.tls.enabled | quote }} + {{- if .Values.config.persistence.database.sql.tls.enabled }} + # SSL_CERTFILE environment variable (CA certificate) + {{- if .Values.config.persistence.database.sql.tls.caFile }} + - name: SSL_CERTFILE + value: {{ .Values.config.persistence.database.sql.tls.caFile | quote }} + {{- else if .Values.config.persistence.database.sql.tls.caFiles }} + - name: SSL_CERTFILE + value: {{ index .Values.config.persistence.database.sql.tls.caFiles 0 | quote }} + {{- end }} + # Client certificate for mutual TLS + {{- if .Values.config.persistence.database.sql.tls.certFile }} + - name: SSL_CLIENT_CERT + value: {{ .Values.config.persistence.database.sql.tls.certFile | quote }} + {{- end }} + # Client private key for mutual TLS + {{- if .Values.config.persistence.database.sql.tls.keyFile }} + - name: SSL_CLIENT_KEY + value: {{ .Values.config.persistence.database.sql.tls.keyFile | quote }} + {{- end }} + {{- end }} + + {{- else if eq $dbDriver "mysql" }} + # MySQL Schema Setup + command: + - sh + - -c + - | + set -e + echo "Starting Cadence schema setup for driver: $DB_DRIVER" + echo "=== Setting up MySQL Schema ===" + + # Build sql-tool command with TLS options + build_mysql_cmd() { + local cmd="cadence-sql-tool --ep $DB_HOST -p $DB_PORT -u $DB_USER -pw $MYSQL_PWD --plugin mysql" + + # Add TLS options if enabled + if [ "$TLS_ENABLED" = "true" ]; then + cmd="$cmd --tls" + if [ -n "$SSL_CERTFILE" ]; then + cmd="$cmd --tls-ca-file $SSL_CERTFILE" + fi + if [ -n "$SSL_CLIENT_CERT" ]; then + cmd="$cmd --tls-cert-file $SSL_CLIENT_CERT" + fi + if [ -n "$SSL_CLIENT_KEY" ]; then + cmd="$cmd --tls-key-file $SSL_CLIENT_KEY" + fi + fi + + echo "$cmd" + } + + # Create main database + echo "Creating main database: $DB_NAME" + $(build_mysql_cmd) create-database --db $DB_NAME + + echo "Setting up main schema version 0.0" + $(build_mysql_cmd) --db $DB_NAME setup-schema -v 0.0 + + echo "Updating main schema to latest version" + $(build_mysql_cmd) --db $DB_NAME update-schema -d $CADENCE_HOME/schema/mysql/v8/cadence/versioned + + # Setup visibility database (only if ES is not enabled) + if [ "$ES_ENABLED" = "false" ]; then + echo "Creating visibility database: $DB_VISIBILITY_NAME" + $(build_mysql_cmd) create-database --db $DB_VISIBILITY_NAME + + echo "Setting up visibility schema version 0.0" + $(build_mysql_cmd) --db $DB_VISIBILITY_NAME setup-schema -v 0.0 + + echo "Updating visibility schema to latest version" + $(build_mysql_cmd) --db $DB_VISIBILITY_NAME update-schema -d $CADENCE_HOME/schema/mysql/v8/visibility/versioned + else + echo "Skipping visibility schema setup (Elasticsearch enabled)" + fi + + echo "Schema setup completed successfully!" + env: + # Common environment variables + - name: DB_DRIVER + value: {{ $dbDriver | quote }} + - name: CADENCE_HOME + value: "/etc/cadence" + - name: ES_ENABLED + value: {{ .Values.config.persistence.elasticsearch.enabled | quote }} + # MySQL specific environment variables + - name: DB_HOST + value: {{ .Values.config.persistence.database.sql.hosts | quote }} + - name: DB_PORT + value: {{ .Values.config.persistence.database.mysql.port | default .Values.config.persistence.database.sql.port | default 3306 | quote }} + - name: DB_NAME + value: {{ .Values.config.persistence.database.sql.dbname | quote }} + - name: DB_VISIBILITY_NAME + value: {{ .Values.config.persistence.database.sql.visibilityDbname | quote }} + - name: DB_USER + value: {{ .Values.config.persistence.database.sql.user | quote }} + # Authentication parameters + - name: MYSQL_PWD + valueFrom: + secretKeyRef: + name: {{ include "cadence.fullname" $ }}-frontend-secrets + key: MYSQL_PWD + # TLS Configuration + - name: TLS_ENABLED + value: {{ .Values.config.persistence.database.sql.tls.enabled | quote }} + {{- if .Values.config.persistence.database.sql.tls.enabled }} + # SSL_CERTFILE environment variable (CA certificate) + {{- if .Values.config.persistence.database.sql.tls.caFile }} + - name: SSL_CERTFILE + value: {{ .Values.config.persistence.database.sql.tls.caFile | quote }} + {{- else if .Values.config.persistence.database.sql.tls.caFiles }} + - name: SSL_CERTFILE + value: {{ index .Values.config.persistence.database.sql.tls.caFiles 0 | quote }} + {{- end }} + # Client certificate for mutual TLS + {{- if .Values.config.persistence.database.sql.tls.certFile }} + - name: SSL_CLIENT_CERT + value: {{ .Values.config.persistence.database.sql.tls.certFile | quote }} + {{- end }} + # Client private key for mutual TLS + {{- if .Values.config.persistence.database.sql.tls.keyFile }} + - name: SSL_CLIENT_KEY + value: {{ .Values.config.persistence.database.sql.tls.keyFile | quote }} + {{- end }} + {{- end }} + {{- end }} + volumeMounts: + {{- with .Values.global.tls.volumeMounts }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.schema.resources }} + resources: + {{- toYaml . | nindent 10 }} + {{- end }} + volumes: + {{- with .Values.global.tls.volumes }} + {{- toYaml . | nindent 6 }} + {{- end }} +{{- end }} \ No newline at end of file From ed0cd00f8aed1f2bb60aed8c730308821a1cbbd7 Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed, 25 Jun 2025 12:27:10 +0200 Subject: [PATCH 03/37] Create schema-setup-job.yaml --- .../cadence/templates/schema-setup-job.yaml | 407 ++++++++++++++++++ 1 file changed, 407 insertions(+) create mode 100644 charts/cadence/templates/schema-setup-job.yaml diff --git a/charts/cadence/templates/schema-setup-job.yaml b/charts/cadence/templates/schema-setup-job.yaml new file mode 100644 index 0000000..35e2cc6 --- /dev/null +++ b/charts/cadence/templates/schema-setup-job.yaml @@ -0,0 +1,407 @@ +{{- if .Values.schema.setupJob.enabled -}} +{{- $dbDriver := .Values.config.persistence.database.driver }} +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "cadence.fullname" . }}-schema-setup + labels: + {{- include "cadence.labels" . | nindent 4 }} + app.kubernetes.io/component: schema-setup + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "-3" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded +spec: + template: + metadata: + labels: + {{- include "cadence.labels" . | nindent 8 }} + app.kubernetes.io/component: schema-setup + spec: + {{- if $.Values.serviceAccount.create }} + serviceAccountName: {{ include "cadence.serviceAccountName" $ }} + {{- end }} + {{- $globalImagePullSecrets := $.Values.global.imagePullSecrets | default list }} + {{- $schemaImagePullSecrets := $.Values.frontend.imagePullSecrets | default list }} + {{- $mergedImagePullSecrets := concat $globalImagePullSecrets $schemaImagePullSecrets }} + {{- if $mergedImagePullSecrets }} + imagePullSecrets: + {{- toYaml $mergedImagePullSecrets | nindent 8 }} + {{- end }} + {{- $globalNodeSelector := $.Values.global.nodeSelector | default dict }} + {{- $schemaNodeSelector := $.Values.schema.nodeSelector | default $globalNodeSelector }} + {{- if $schemaNodeSelector }} + nodeSelector: + {{- toYaml $schemaNodeSelector | nindent 8 }} + {{- end }} + {{- $globalAffinity := $.Values.global.affinity | default dict }} + {{- $schemaAffinity := $.Values.schema.affinity | default $globalAffinity }} + {{- if $schemaAffinity }} + affinity: + {{- toYaml $schemaAffinity | nindent 8 }} + {{- end }} + {{- $globalTolerations := $.Values.global.tolerations | default list }} + {{- $schemaTolerations := $.Values.schema.tolerations | default $globalTolerations }} + {{- if $schemaTolerations }} + tolerations: + {{- toYaml $schemaTolerations | nindent 8 }} + {{- end }} + restartPolicy: OnFailure + containers: + - name: cadence-schema-setup + {{- $globalImage := .Values.global.image | default dict }} + {{- $schemaImage := $.Values.frontend.image | default dict }} + {{- $repository := $schemaImage.repository | default $globalImage.repository }} + {{- $tag := $schemaImage.tag | default $globalImage.tag }} + image: {{ $repository }}:{{ $tag }} + {{- $pullPolicy := $schemaImage.pullPolicy | default $globalImage.pullPolicy | default "IfNotPresent" }} + imagePullPolicy: {{ $pullPolicy }} + {{- if eq $dbDriver "cassandra" }} + # Cassandra Schema Setup + command: + - sh + - -c + - | + set -e + echo "Starting Cadence schema setup for driver: $DB_DRIVER" + echo "=== Setting up Cassandra Schema ===" + + # Build cassandra-tool command with TLS options + build_cassandra_cmd() { + local cmd="cadence-cassandra-tool --ep $DB_HOST" + + # Add authentication + if [ -n "$DB_USER" ]; then + cmd="$cmd -u $DB_USER" + fi + if [ -n "$CASSANDRA_PASSWORD" ]; then + cmd="$cmd -pw $CASSANDRA_PASSWORD" + fi + + # Add allowed authenticators from environment variable + if [ -n "$ALLOWED_AUTHENTICATORS" ]; then + cmd="$cmd $ALLOWED_AUTHENTICATORS" + fi + + # Add TLS options if enabled + if [ "$TLS_ENABLED" = "true" ]; then + cmd="$cmd --tls" + if [ -n "$SSL_CERTFILE" ]; then + cmd="$cmd --tls-ca-file $SSL_CERTFILE" + fi + if [ -n "$SSL_CLIENT_CERT" ]; then + cmd="$cmd --tls-cert-file $SSL_CLIENT_CERT" + fi + if [ -n "$SSL_CLIENT_KEY" ]; then + cmd="$cmd --tls-key-file $SSL_CLIENT_KEY" + fi + fi + + echo "$cmd" + } + + # Setup main database schema + echo "Creating main keyspace: $DB_NAME" + $(build_cassandra_cmd) create -k $DB_NAME --rf $REPLICATION_FACTOR + + echo "Setting up main schema version 0.0" + $(build_cassandra_cmd) -k $DB_NAME setup-schema -v 0.0 + + echo "Updating main schema to latest version" + $(build_cassandra_cmd) -k $DB_NAME update-schema -d $CADENCE_HOME/schema/cassandra/cadence/versioned + + # Setup visibility database schema (only if ES is not enabled) + if [ "$ES_ENABLED" = "false" ]; then + echo "Creating visibility keyspace: $DB_VISIBILITY_NAME" + $(build_cassandra_cmd) create -k $DB_VISIBILITY_NAME --rf $REPLICATION_FACTOR + + echo "Setting up visibility schema version 0.0" + $(build_cassandra_cmd) -k $DB_VISIBILITY_NAME setup-schema -v 0.0 + + echo "Updating visibility schema to latest version" + $(build_cassandra_cmd) -k $DB_VISIBILITY_NAME update-schema -d $CADENCE_HOME/schema/cassandra/visibility/versioned + else + echo "Skipping visibility schema setup (Elasticsearch enabled)" + fi + + echo "Schema setup completed successfully!" + env: + # Common environment variables + - name: DB_DRIVER + value: {{ $dbDriver | quote }} + - name: CADENCE_HOME + value: "/etc/cadence" + - name: ES_ENABLED + value: {{ .Values.config.persistence.elasticsearch.enabled | quote }} + # Cassandra specific environment variables + - name: DB_HOST + value: {{ .Values.config.persistence.database.cassandra.hosts | quote }} + - name: DB_NAME + value: {{ .Values.config.persistence.database.cassandra.keyspace | quote }} + - name: DB_VISIBILITY_NAME + value: {{ .Values.config.persistence.database.cassandra.visibilityKeyspace | quote }} + - name: REPLICATION_FACTOR + value: {{ .Values.config.persistence.database.cassandra.replicationFactor | default 1 | quote }} + # Allowed authenticators (build --aa parameters) + - name: ALLOWED_AUTHENTICATORS + value: {{ if .Values.config.persistence.database.cassandra.allowedAuthenticators }}{{ range $index, $auth := .Values.config.persistence.database.cassandra.allowedAuthenticators }}{{ if $index }} {{ end }}--aa {{ $auth | quote }}{{ end }}{{ else }}"--aa org.apache.cassandra.auth.PasswordAuthenticator"{{ end }} + # Authentication parameters (conditional) + {{- if .Values.config.persistence.database.cassandra.user }} + - name: DB_USER + value: {{ .Values.config.persistence.database.cassandra.user | quote }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.password }} + - name: CASSANDRA_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "cadence.fullname" $ }}-frontend-secrets + key: CASSANDRA_PASSWORD + {{- end }} + # TLS Configuration + - name: TLS_ENABLED + value: {{ .Values.config.persistence.database.cassandra.tls.enabled | quote }} + {{- if .Values.config.persistence.database.cassandra.tls.enabled }} + # SSL_CERTFILE environment variable (CA certificate) + {{- if .Values.config.persistence.database.cassandra.tls.caFile }} + - name: SSL_CERTFILE + value: {{ .Values.config.persistence.database.cassandra.tls.caFile | quote }} + {{- else if .Values.config.persistence.database.cassandra.tls.caFiles }} + - name: SSL_CERTFILE + value: {{ index .Values.config.persistence.database.cassandra.tls.caFiles 0 | quote }} + {{- end }} + # Client certificate for mutual TLS + {{- if .Values.config.persistence.database.cassandra.tls.certFile }} + - name: SSL_CLIENT_CERT + value: {{ .Values.config.persistence.database.cassandra.tls.certFile | quote }} + {{- end }} + # Client private key for mutual TLS + {{- if .Values.config.persistence.database.cassandra.tls.keyFile }} + - name: SSL_CLIENT_KEY + value: {{ .Values.config.persistence.database.cassandra.tls.keyFile | quote }} + {{- end }} + {{- end }} + + {{- else if eq $dbDriver "postgres" }} + # PostgreSQL Schema Setup + command: + - sh + - -c + - | + set -e + echo "Starting Cadence schema setup for driver: $DB_DRIVER" + echo "=== Setting up PostgreSQL Schema ===" + + # Build sql-tool command with TLS options + build_postgres_cmd() { + local cmd="cadence-sql-tool --ep $DB_HOST -p $DB_PORT -u $DB_USER -pw $POSTGRES_PWD --plugin postgres" + + # Add TLS options if enabled + if [ "$TLS_ENABLED" = "true" ]; then + cmd="$cmd --tls" + if [ -n "$SSL_CERTFILE" ]; then + cmd="$cmd --tls-ca-file $SSL_CERTFILE" + fi + if [ -n "$SSL_CLIENT_CERT" ]; then + cmd="$cmd --tls-cert-file $SSL_CLIENT_CERT" + fi + if [ -n "$SSL_CLIENT_KEY" ]; then + cmd="$cmd --tls-key-file $SSL_CLIENT_KEY" + fi + fi + + echo "$cmd" + } + + # Create main database + echo "Creating main database: $DB_NAME" + $(build_postgres_cmd) create-database --db $DB_NAME + + echo "Setting up main schema version 0.0" + $(build_postgres_cmd) --db $DB_NAME setup-schema -v 0.0 + + echo "Updating main schema to latest version" + $(build_postgres_cmd) --db $DB_NAME update-schema -d $CADENCE_HOME/schema/postgres/cadence/versioned + + # Setup visibility database (only if ES is not enabled) + if [ "$ES_ENABLED" = "false" ]; then + echo "Creating visibility database: $DB_VISIBILITY_NAME" + $(build_postgres_cmd) create-database --db $DB_VISIBILITY_NAME + + echo "Setting up visibility schema version 0.0" + $(build_postgres_cmd) --db $DB_VISIBILITY_NAME setup-schema -v 0.0 + + echo "Updating visibility schema to latest version" + $(build_postgres_cmd) --db $DB_VISIBILITY_NAME update-schema -d $CADENCE_HOME/schema/postgres/visibility/versioned + else + echo "Skipping visibility schema setup (Elasticsearch enabled)" + fi + + echo "Schema setup completed successfully!" + env: + # Common environment variables + - name: DB_DRIVER + value: {{ $dbDriver | quote }} + - name: CADENCE_HOME + value: "/etc/cadence" + - name: ES_ENABLED + value: {{ .Values.config.persistence.elasticsearch.enabled | quote }} + # PostgreSQL specific environment variables + - name: DB_HOST + value: {{ .Values.config.persistence.database.sql.hosts | quote }} + - name: DB_PORT + value: {{ .Values.config.persistence.database.postgres.port | default .Values.config.persistence.database.sql.port | default 5432 | quote }} + - name: DB_NAME + value: {{ .Values.config.persistence.database.sql.dbname | quote }} + - name: DB_VISIBILITY_NAME + value: {{ .Values.config.persistence.database.sql.visibilityDbname | quote }} + - name: DB_USER + value: {{ .Values.config.persistence.database.sql.user | quote }} + # Authentication parameters + - name: POSTGRES_PWD + valueFrom: + secretKeyRef: + name: {{ include "cadence.fullname" $ }}-frontend-secrets + key: POSTGRES_PWD + # TLS Configuration + - name: TLS_ENABLED + value: {{ .Values.config.persistence.database.sql.tls.enabled | quote }} + {{- if .Values.config.persistence.database.sql.tls.enabled }} + # SSL_CERTFILE environment variable (CA certificate) + {{- if .Values.config.persistence.database.sql.tls.caFile }} + - name: SSL_CERTFILE + value: {{ .Values.config.persistence.database.sql.tls.caFile | quote }} + {{- else if .Values.config.persistence.database.sql.tls.caFiles }} + - name: SSL_CERTFILE + value: {{ index .Values.config.persistence.database.sql.tls.caFiles 0 | quote }} + {{- end }} + # Client certificate for mutual TLS + {{- if .Values.config.persistence.database.sql.tls.certFile }} + - name: SSL_CLIENT_CERT + value: {{ .Values.config.persistence.database.sql.tls.certFile | quote }} + {{- end }} + # Client private key for mutual TLS + {{- if .Values.config.persistence.database.sql.tls.keyFile }} + - name: SSL_CLIENT_KEY + value: {{ .Values.config.persistence.database.sql.tls.keyFile | quote }} + {{- end }} + {{- end }} + + {{- else if eq $dbDriver "mysql" }} + # MySQL Schema Setup + command: + - sh + - -c + - | + set -e + echo "Starting Cadence schema setup for driver: $DB_DRIVER" + echo "=== Setting up MySQL Schema ===" + + # Build sql-tool command with TLS options + build_mysql_cmd() { + local cmd="cadence-sql-tool --ep $DB_HOST -p $DB_PORT -u $DB_USER -pw $MYSQL_PWD --plugin mysql" + + # Add TLS options if enabled + if [ "$TLS_ENABLED" = "true" ]; then + cmd="$cmd --tls" + if [ -n "$SSL_CERTFILE" ]; then + cmd="$cmd --tls-ca-file $SSL_CERTFILE" + fi + if [ -n "$SSL_CLIENT_CERT" ]; then + cmd="$cmd --tls-cert-file $SSL_CLIENT_CERT" + fi + if [ -n "$SSL_CLIENT_KEY" ]; then + cmd="$cmd --tls-key-file $SSL_CLIENT_KEY" + fi + fi + + echo "$cmd" + } + + # Create main database + echo "Creating main database: $DB_NAME" + $(build_mysql_cmd) create-database --db $DB_NAME + + echo "Setting up main schema version 0.0" + $(build_mysql_cmd) --db $DB_NAME setup-schema -v 0.0 + + echo "Updating main schema to latest version" + $(build_mysql_cmd) --db $DB_NAME update-schema -d $CADENCE_HOME/schema/mysql/v8/cadence/versioned + + # Setup visibility database (only if ES is not enabled) + if [ "$ES_ENABLED" = "false" ]; then + echo "Creating visibility database: $DB_VISIBILITY_NAME" + $(build_mysql_cmd) create-database --db $DB_VISIBILITY_NAME + + echo "Setting up visibility schema version 0.0" + $(build_mysql_cmd) --db $DB_VISIBILITY_NAME setup-schema -v 0.0 + + echo "Updating visibility schema to latest version" + $(build_mysql_cmd) --db $DB_VISIBILITY_NAME update-schema -d $CADENCE_HOME/schema/mysql/v8/visibility/versioned + else + echo "Skipping visibility schema setup (Elasticsearch enabled)" + fi + + echo "Schema setup completed successfully!" + env: + # Common environment variables + - name: DB_DRIVER + value: {{ $dbDriver | quote }} + - name: CADENCE_HOME + value: "/etc/cadence" + - name: ES_ENABLED + value: {{ .Values.config.persistence.elasticsearch.enabled | quote }} + # MySQL specific environment variables + - name: DB_HOST + value: {{ .Values.config.persistence.database.sql.hosts | quote }} + - name: DB_PORT + value: {{ .Values.config.persistence.database.mysql.port | default .Values.config.persistence.database.sql.port | default 3306 | quote }} + - name: DB_NAME + value: {{ .Values.config.persistence.database.sql.dbname | quote }} + - name: DB_VISIBILITY_NAME + value: {{ .Values.config.persistence.database.sql.visibilityDbname | quote }} + - name: DB_USER + value: {{ .Values.config.persistence.database.sql.user | quote }} + # Authentication parameters + - name: MYSQL_PWD + valueFrom: + secretKeyRef: + name: {{ include "cadence.fullname" $ }}-frontend-secrets + key: MYSQL_PWD + # TLS Configuration + - name: TLS_ENABLED + value: {{ .Values.config.persistence.database.sql.tls.enabled | quote }} + {{- if .Values.config.persistence.database.sql.tls.enabled }} + # SSL_CERTFILE environment variable (CA certificate) + {{- if .Values.config.persistence.database.sql.tls.caFile }} + - name: SSL_CERTFILE + value: {{ .Values.config.persistence.database.sql.tls.caFile | quote }} + {{- else if .Values.config.persistence.database.sql.tls.caFiles }} + - name: SSL_CERTFILE + value: {{ index .Values.config.persistence.database.sql.tls.caFiles 0 | quote }} + {{- end }} + # Client certificate for mutual TLS + {{- if .Values.config.persistence.database.sql.tls.certFile }} + - name: SSL_CLIENT_CERT + value: {{ .Values.config.persistence.database.sql.tls.certFile | quote }} + {{- end }} + # Client private key for mutual TLS + {{- if .Values.config.persistence.database.sql.tls.keyFile }} + - name: SSL_CLIENT_KEY + value: {{ .Values.config.persistence.database.sql.tls.keyFile | quote }} + {{- end }} + {{- end }} + {{- end }} + volumeMounts: + {{- with .Values.global.tls.volumeMounts }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.schema.resources }} + resources: + {{- toYaml . | nindent 10 }} + {{- end }} + volumes: + {{- with .Values.global.tls.volumes }} + {{- toYaml . | nindent 6 }} + {{- end }} +{{- end }} \ No newline at end of file From c40e481edf187f9384210b485f0fbb3f95d02162 Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed, 25 Jun 2025 12:27:40 +0200 Subject: [PATCH 04/37] Create schema-update-job.yaml --- .../cadence/templates/schema-update-job.yaml | 407 ++++++++++++++++++ 1 file changed, 407 insertions(+) create mode 100644 charts/cadence/templates/schema-update-job.yaml diff --git a/charts/cadence/templates/schema-update-job.yaml b/charts/cadence/templates/schema-update-job.yaml new file mode 100644 index 0000000..8db53bb --- /dev/null +++ b/charts/cadence/templates/schema-update-job.yaml @@ -0,0 +1,407 @@ +{{- if .Values.schema.updateJob.enabled -}} +{{- $dbDriver := .Values.config.persistence.database.driver }} +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "cadence.fullname" . }}-schema-update + labels: + {{- include "cadence.labels" . | nindent 4 }} + app.kubernetes.io/component: schema-update + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "-3" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded +spec: + template: + metadata: + labels: + {{- include "cadence.labels" . | nindent 8 }} + app.kubernetes.io/component: schema-setup + spec: + {{- if $.Values.serviceAccount.create }} + serviceAccountName: {{ include "cadence.serviceAccountName" $ }} + {{- end }} + {{- $globalImagePullSecrets := $.Values.global.imagePullSecrets | default list }} + {{- $schemaImagePullSecrets := $.Values.frontend.imagePullSecrets | default list }} + {{- $mergedImagePullSecrets := concat $globalImagePullSecrets $schemaImagePullSecrets }} + {{- if $mergedImagePullSecrets }} + imagePullSecrets: + {{- toYaml $mergedImagePullSecrets | nindent 8 }} + {{- end }} + {{- $globalNodeSelector := $.Values.global.nodeSelector | default dict }} + {{- $schemaNodeSelector := $.Values.schema.nodeSelector | default $globalNodeSelector }} + {{- if $schemaNodeSelector }} + nodeSelector: + {{- toYaml $schemaNodeSelector | nindent 8 }} + {{- end }} + {{- $globalAffinity := $.Values.global.affinity | default dict }} + {{- $schemaAffinity := $.Values.schema.affinity | default $globalAffinity }} + {{- if $schemaAffinity }} + affinity: + {{- toYaml $schemaAffinity | nindent 8 }} + {{- end }} + {{- $globalTolerations := $.Values.global.tolerations | default list }} + {{- $schemaTolerations := $.Values.schema.tolerations | default $globalTolerations }} + {{- if $schemaTolerations }} + tolerations: + {{- toYaml $schemaTolerations | nindent 8 }} + {{- end }} + restartPolicy: OnFailure + containers: + - name: cadence-schema-update + {{- $globalImage := .Values.global.image | default dict }} + {{- $schemaImage := $.Values.frontend.image | default dict }} + {{- $repository := $schemaImage.repository | default $globalImage.repository }} + {{- $tag := $schemaImage.tag | default $globalImage.tag }} + image: {{ $repository }}:{{ $tag }} + {{- $pullPolicy := $schemaImage.pullPolicy | default $globalImage.pullPolicy | default "IfNotPresent" }} + imagePullPolicy: {{ $pullPolicy }} + {{- if eq $dbDriver "cassandra" }} + # Cassandra Schema Setup + command: + - sh + - -c + - | + set -e + echo "Starting Cadence schema setup for driver: $DB_DRIVER" + echo "=== Setting up Cassandra Schema ===" + + # Build cassandra-tool command with TLS options + build_cassandra_cmd() { + local cmd="cadence-cassandra-tool --ep $DB_HOST" + + # Add authentication + if [ -n "$DB_USER" ]; then + cmd="$cmd -u $DB_USER" + fi + if [ -n "$CASSANDRA_PASSWORD" ]; then + cmd="$cmd -pw $CASSANDRA_PASSWORD" + fi + + # Add allowed authenticators from environment variable + if [ -n "$ALLOWED_AUTHENTICATORS" ]; then + cmd="$cmd $ALLOWED_AUTHENTICATORS" + fi + + # Add TLS options if enabled + if [ "$TLS_ENABLED" = "true" ]; then + cmd="$cmd --tls" + if [ -n "$SSL_CERTFILE" ]; then + cmd="$cmd --tls-ca-file $SSL_CERTFILE" + fi + if [ -n "$SSL_CLIENT_CERT" ]; then + cmd="$cmd --tls-cert-file $SSL_CLIENT_CERT" + fi + if [ -n "$SSL_CLIENT_KEY" ]; then + cmd="$cmd --tls-key-file $SSL_CLIENT_KEY" + fi + fi + + echo "$cmd" + } + + # Setup main database schema + echo "Creating main keyspace: $DB_NAME" + $(build_cassandra_cmd) create -k $DB_NAME --rf $REPLICATION_FACTOR + + echo "Setting up main schema version 0.0" + $(build_cassandra_cmd) -k $DB_NAME setup-schema -v 0.0 + + echo "Updating main schema to latest version" + $(build_cassandra_cmd) -k $DB_NAME update-schema -d $CADENCE_HOME/schema/cassandra/cadence/versioned + + # Setup visibility database schema (only if ES is not enabled) + if [ "$ES_ENABLED" = "false" ]; then + echo "Creating visibility keyspace: $DB_VISIBILITY_NAME" + $(build_cassandra_cmd) create -k $DB_VISIBILITY_NAME --rf $REPLICATION_FACTOR + + echo "Setting up visibility schema version 0.0" + $(build_cassandra_cmd) -k $DB_VISIBILITY_NAME setup-schema -v 0.0 + + echo "Updating visibility schema to latest version" + $(build_cassandra_cmd) -k $DB_VISIBILITY_NAME update-schema -d $CADENCE_HOME/schema/cassandra/visibility/versioned + else + echo "Skipping visibility schema setup (Elasticsearch enabled)" + fi + + echo "Schema setup completed successfully!" + env: + # Common environment variables + - name: DB_DRIVER + value: {{ $dbDriver | quote }} + - name: CADENCE_HOME + value: "/etc/cadence" + - name: ES_ENABLED + value: {{ .Values.config.persistence.elasticsearch.enabled | quote }} + # Cassandra specific environment variables + - name: DB_HOST + value: {{ .Values.config.persistence.database.cassandra.hosts | quote }} + - name: DB_NAME + value: {{ .Values.config.persistence.database.cassandra.keyspace | quote }} + - name: DB_VISIBILITY_NAME + value: {{ .Values.config.persistence.database.cassandra.visibilityKeyspace | quote }} + - name: REPLICATION_FACTOR + value: {{ .Values.config.persistence.database.cassandra.replicationFactor | default 1 | quote }} + # Allowed authenticators (build --aa parameters) + - name: ALLOWED_AUTHENTICATORS + value: {{ if .Values.config.persistence.database.cassandra.allowedAuthenticators }}{{ range $index, $auth := .Values.config.persistence.database.cassandra.allowedAuthenticators }}{{ if $index }} {{ end }}--aa {{ $auth | quote }}{{ end }}{{ else }}"--aa org.apache.cassandra.auth.PasswordAuthenticator"{{ end }} + # Authentication parameters (conditional) + {{- if .Values.config.persistence.database.cassandra.user }} + - name: DB_USER + value: {{ .Values.config.persistence.database.cassandra.user | quote }} + {{- end }} + {{- if .Values.config.persistence.database.cassandra.password }} + - name: CASSANDRA_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "cadence.fullname" $ }}-frontend-secrets + key: CASSANDRA_PASSWORD + {{- end }} + # TLS Configuration + - name: TLS_ENABLED + value: {{ .Values.config.persistence.database.cassandra.tls.enabled | quote }} + {{- if .Values.config.persistence.database.cassandra.tls.enabled }} + # SSL_CERTFILE environment variable (CA certificate) + {{- if .Values.config.persistence.database.cassandra.tls.caFile }} + - name: SSL_CERTFILE + value: {{ .Values.config.persistence.database.cassandra.tls.caFile | quote }} + {{- else if .Values.config.persistence.database.cassandra.tls.caFiles }} + - name: SSL_CERTFILE + value: {{ index .Values.config.persistence.database.cassandra.tls.caFiles 0 | quote }} + {{- end }} + # Client certificate for mutual TLS + {{- if .Values.config.persistence.database.cassandra.tls.certFile }} + - name: SSL_CLIENT_CERT + value: {{ .Values.config.persistence.database.cassandra.tls.certFile | quote }} + {{- end }} + # Client private key for mutual TLS + {{- if .Values.config.persistence.database.cassandra.tls.keyFile }} + - name: SSL_CLIENT_KEY + value: {{ .Values.config.persistence.database.cassandra.tls.keyFile | quote }} + {{- end }} + {{- end }} + + {{- else if eq $dbDriver "postgres" }} + # PostgreSQL Schema Setup + command: + - sh + - -c + - | + set -e + echo "Starting Cadence schema setup for driver: $DB_DRIVER" + echo "=== Setting up PostgreSQL Schema ===" + + # Build sql-tool command with TLS options + build_postgres_cmd() { + local cmd="cadence-sql-tool --ep $DB_HOST -p $DB_PORT -u $DB_USER -pw $POSTGRES_PWD --plugin postgres" + + # Add TLS options if enabled + if [ "$TLS_ENABLED" = "true" ]; then + cmd="$cmd --tls" + if [ -n "$SSL_CERTFILE" ]; then + cmd="$cmd --tls-ca-file $SSL_CERTFILE" + fi + if [ -n "$SSL_CLIENT_CERT" ]; then + cmd="$cmd --tls-cert-file $SSL_CLIENT_CERT" + fi + if [ -n "$SSL_CLIENT_KEY" ]; then + cmd="$cmd --tls-key-file $SSL_CLIENT_KEY" + fi + fi + + echo "$cmd" + } + + # Create main database + echo "Creating main database: $DB_NAME" + $(build_postgres_cmd) create-database --db $DB_NAME + + echo "Setting up main schema version 0.0" + $(build_postgres_cmd) --db $DB_NAME setup-schema -v 0.0 + + echo "Updating main schema to latest version" + $(build_postgres_cmd) --db $DB_NAME update-schema -d $CADENCE_HOME/schema/postgres/cadence/versioned + + # Setup visibility database (only if ES is not enabled) + if [ "$ES_ENABLED" = "false" ]; then + echo "Creating visibility database: $DB_VISIBILITY_NAME" + $(build_postgres_cmd) create-database --db $DB_VISIBILITY_NAME + + echo "Setting up visibility schema version 0.0" + $(build_postgres_cmd) --db $DB_VISIBILITY_NAME setup-schema -v 0.0 + + echo "Updating visibility schema to latest version" + $(build_postgres_cmd) --db $DB_VISIBILITY_NAME update-schema -d $CADENCE_HOME/schema/postgres/visibility/versioned + else + echo "Skipping visibility schema setup (Elasticsearch enabled)" + fi + + echo "Schema setup completed successfully!" + env: + # Common environment variables + - name: DB_DRIVER + value: {{ $dbDriver | quote }} + - name: CADENCE_HOME + value: "/etc/cadence" + - name: ES_ENABLED + value: {{ .Values.config.persistence.elasticsearch.enabled | quote }} + # PostgreSQL specific environment variables + - name: DB_HOST + value: {{ .Values.config.persistence.database.sql.hosts | quote }} + - name: DB_PORT + value: {{ .Values.config.persistence.database.postgres.port | default .Values.config.persistence.database.sql.port | default 5432 | quote }} + - name: DB_NAME + value: {{ .Values.config.persistence.database.sql.dbname | quote }} + - name: DB_VISIBILITY_NAME + value: {{ .Values.config.persistence.database.sql.visibilityDbname | quote }} + - name: DB_USER + value: {{ .Values.config.persistence.database.sql.user | quote }} + # Authentication parameters + - name: POSTGRES_PWD + valueFrom: + secretKeyRef: + name: {{ include "cadence.fullname" $ }}-frontend-secrets + key: POSTGRES_PWD + # TLS Configuration + - name: TLS_ENABLED + value: {{ .Values.config.persistence.database.sql.tls.enabled | quote }} + {{- if .Values.config.persistence.database.sql.tls.enabled }} + # SSL_CERTFILE environment variable (CA certificate) + {{- if .Values.config.persistence.database.sql.tls.caFile }} + - name: SSL_CERTFILE + value: {{ .Values.config.persistence.database.sql.tls.caFile | quote }} + {{- else if .Values.config.persistence.database.sql.tls.caFiles }} + - name: SSL_CERTFILE + value: {{ index .Values.config.persistence.database.sql.tls.caFiles 0 | quote }} + {{- end }} + # Client certificate for mutual TLS + {{- if .Values.config.persistence.database.sql.tls.certFile }} + - name: SSL_CLIENT_CERT + value: {{ .Values.config.persistence.database.sql.tls.certFile | quote }} + {{- end }} + # Client private key for mutual TLS + {{- if .Values.config.persistence.database.sql.tls.keyFile }} + - name: SSL_CLIENT_KEY + value: {{ .Values.config.persistence.database.sql.tls.keyFile | quote }} + {{- end }} + {{- end }} + + {{- else if eq $dbDriver "mysql" }} + # MySQL Schema Setup + command: + - sh + - -c + - | + set -e + echo "Starting Cadence schema setup for driver: $DB_DRIVER" + echo "=== Setting up MySQL Schema ===" + + # Build sql-tool command with TLS options + build_mysql_cmd() { + local cmd="cadence-sql-tool --ep $DB_HOST -p $DB_PORT -u $DB_USER -pw $MYSQL_PWD --plugin mysql" + + # Add TLS options if enabled + if [ "$TLS_ENABLED" = "true" ]; then + cmd="$cmd --tls" + if [ -n "$SSL_CERTFILE" ]; then + cmd="$cmd --tls-ca-file $SSL_CERTFILE" + fi + if [ -n "$SSL_CLIENT_CERT" ]; then + cmd="$cmd --tls-cert-file $SSL_CLIENT_CERT" + fi + if [ -n "$SSL_CLIENT_KEY" ]; then + cmd="$cmd --tls-key-file $SSL_CLIENT_KEY" + fi + fi + + echo "$cmd" + } + + # Create main database + echo "Creating main database: $DB_NAME" + $(build_mysql_cmd) create-database --db $DB_NAME + + echo "Setting up main schema version 0.0" + $(build_mysql_cmd) --db $DB_NAME setup-schema -v 0.0 + + echo "Updating main schema to latest version" + $(build_mysql_cmd) --db $DB_NAME update-schema -d $CADENCE_HOME/schema/mysql/v8/cadence/versioned + + # Setup visibility database (only if ES is not enabled) + if [ "$ES_ENABLED" = "false" ]; then + echo "Creating visibility database: $DB_VISIBILITY_NAME" + $(build_mysql_cmd) create-database --db $DB_VISIBILITY_NAME + + echo "Setting up visibility schema version 0.0" + $(build_mysql_cmd) --db $DB_VISIBILITY_NAME setup-schema -v 0.0 + + echo "Updating visibility schema to latest version" + $(build_mysql_cmd) --db $DB_VISIBILITY_NAME update-schema -d $CADENCE_HOME/schema/mysql/v8/visibility/versioned + else + echo "Skipping visibility schema setup (Elasticsearch enabled)" + fi + + echo "Schema setup completed successfully!" + env: + # Common environment variables + - name: DB_DRIVER + value: {{ $dbDriver | quote }} + - name: CADENCE_HOME + value: "/etc/cadence" + - name: ES_ENABLED + value: {{ .Values.config.persistence.elasticsearch.enabled | quote }} + # MySQL specific environment variables + - name: DB_HOST + value: {{ .Values.config.persistence.database.sql.hosts | quote }} + - name: DB_PORT + value: {{ .Values.config.persistence.database.mysql.port | default .Values.config.persistence.database.sql.port | default 3306 | quote }} + - name: DB_NAME + value: {{ .Values.config.persistence.database.sql.dbname | quote }} + - name: DB_VISIBILITY_NAME + value: {{ .Values.config.persistence.database.sql.visibilityDbname | quote }} + - name: DB_USER + value: {{ .Values.config.persistence.database.sql.user | quote }} + # Authentication parameters + - name: MYSQL_PWD + valueFrom: + secretKeyRef: + name: {{ include "cadence.fullname" $ }}-frontend-secrets + key: MYSQL_PWD + # TLS Configuration + - name: TLS_ENABLED + value: {{ .Values.config.persistence.database.sql.tls.enabled | quote }} + {{- if .Values.config.persistence.database.sql.tls.enabled }} + # SSL_CERTFILE environment variable (CA certificate) + {{- if .Values.config.persistence.database.sql.tls.caFile }} + - name: SSL_CERTFILE + value: {{ .Values.config.persistence.database.sql.tls.caFile | quote }} + {{- else if .Values.config.persistence.database.sql.tls.caFiles }} + - name: SSL_CERTFILE + value: {{ index .Values.config.persistence.database.sql.tls.caFiles 0 | quote }} + {{- end }} + # Client certificate for mutual TLS + {{- if .Values.config.persistence.database.sql.tls.certFile }} + - name: SSL_CLIENT_CERT + value: {{ .Values.config.persistence.database.sql.tls.certFile | quote }} + {{- end }} + # Client private key for mutual TLS + {{- if .Values.config.persistence.database.sql.tls.keyFile }} + - name: SSL_CLIENT_KEY + value: {{ .Values.config.persistence.database.sql.tls.keyFile | quote }} + {{- end }} + {{- end }} + {{- end }} + volumeMounts: + {{- with .Values.global.tls.volumeMounts }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.schema.resources }} + resources: + {{- toYaml . | nindent 10 }} + {{- end }} + volumes: + {{- with .Values.global.tls.volumes }} + {{- toYaml . | nindent 6 }} + {{- end }} +{{- end }} \ No newline at end of file From 40933c563404485c6ab708c16b782e8b69bfc3b3 Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed, 25 Jun 2025 12:28:09 +0200 Subject: [PATCH 05/37] Schema changes in values.yaml --- charts/cadence/values.yaml | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/charts/cadence/values.yaml b/charts/cadence/values.yaml index 3c8ff39..4569b09 100644 --- a/charts/cadence/values.yaml +++ b/charts/cadence/values.yaml @@ -780,6 +780,8 @@ config: keyspace: "cadence" # -- Cassandra keyspace for visibility data visibilityKeyspace: "cadence_visibility" + # -- Number of replicas of cassandra deployed to ensure reliability and fault tolerance across the cluster + replicationFactor: 1 # -- Cassandra username user: "cassandra" # -- Cassandra password @@ -1115,16 +1117,33 @@ dynamicConfig: #################### SCHEMA CONFIGURATION ##################### ############################################################### schema: + # -- Schema Jobs resources + resources: {} + # limits: + # cpu: "500m" + # memory: "1Gi" + # requests: + # cpu: "500m" + # memory: "1Gi" + # -- Schema Jobs affinity rules + affinity: {} + # -- Schema Jobs tolerations + tolerations: [] + # -- Schema Jobs node selector + nodeSelector: {} + + createJob: + enabled: true + setupJob: + enabled: true + updateJob: + enabled: false + version: default: "0.42" visibility: "0.9" - # createJob: - # enabled: false - # No support for mysql & postgresql yet. - setupJob: - enabled: true - # updateJob: - # enabled: false + + # -- Images used for validate schema creation on cadence-server checkSchema: cassandra: image: From cf90e49043e41708e3258e10fe737855b986f3c2 Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed, 25 Jun 2025 12:35:03 +0200 Subject: [PATCH 06/37] delete 3 job and converted in 1 --- ...create-job.yaml => schema-server-job.yaml} | 8 +- .../cadence/templates/schema-setup-job.yaml | 407 ------------------ .../cadence/templates/schema-update-job.yaml | 407 ------------------ 3 files changed, 4 insertions(+), 818 deletions(-) rename charts/cadence/templates/{schema-create-job.yaml => schema-server-job.yaml} (98%) delete mode 100644 charts/cadence/templates/schema-setup-job.yaml delete mode 100644 charts/cadence/templates/schema-update-job.yaml diff --git a/charts/cadence/templates/schema-create-job.yaml b/charts/cadence/templates/schema-server-job.yaml similarity index 98% rename from charts/cadence/templates/schema-create-job.yaml rename to charts/cadence/templates/schema-server-job.yaml index cfa23d2..a19a915 100644 --- a/charts/cadence/templates/schema-create-job.yaml +++ b/charts/cadence/templates/schema-server-job.yaml @@ -4,10 +4,10 @@ apiVersion: batch/v1 kind: Job metadata: - name: {{ include "cadence.fullname" . }}-schema-create + name: {{ include "cadence.fullname" . }}-schema-server labels: {{- include "cadence.labels" . | nindent 4 }} - app.kubernetes.io/component: schema-create + app.kubernetes.io/component: schema-server annotations: "helm.sh/hook": pre-install,pre-upgrade "helm.sh/hook-weight": "-3" @@ -17,7 +17,7 @@ spec: metadata: labels: {{- include "cadence.labels" . | nindent 8 }} - app.kubernetes.io/component: schema-setup + app.kubernetes.io/component: schema-server spec: {{- if $.Values.serviceAccount.create }} serviceAccountName: {{ include "cadence.serviceAccountName" $ }} @@ -49,7 +49,7 @@ spec: {{- end }} restartPolicy: OnFailure containers: - - name: cadence-schema-create + - name: cadence-schema-server {{- $globalImage := .Values.global.image | default dict }} {{- $schemaImage := $.Values.frontend.image | default dict }} {{- $repository := $schemaImage.repository | default $globalImage.repository }} diff --git a/charts/cadence/templates/schema-setup-job.yaml b/charts/cadence/templates/schema-setup-job.yaml deleted file mode 100644 index 35e2cc6..0000000 --- a/charts/cadence/templates/schema-setup-job.yaml +++ /dev/null @@ -1,407 +0,0 @@ -{{- if .Values.schema.setupJob.enabled -}} -{{- $dbDriver := .Values.config.persistence.database.driver }} ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: {{ include "cadence.fullname" . }}-schema-setup - labels: - {{- include "cadence.labels" . | nindent 4 }} - app.kubernetes.io/component: schema-setup - annotations: - "helm.sh/hook": pre-install,pre-upgrade - "helm.sh/hook-weight": "-3" - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded -spec: - template: - metadata: - labels: - {{- include "cadence.labels" . | nindent 8 }} - app.kubernetes.io/component: schema-setup - spec: - {{- if $.Values.serviceAccount.create }} - serviceAccountName: {{ include "cadence.serviceAccountName" $ }} - {{- end }} - {{- $globalImagePullSecrets := $.Values.global.imagePullSecrets | default list }} - {{- $schemaImagePullSecrets := $.Values.frontend.imagePullSecrets | default list }} - {{- $mergedImagePullSecrets := concat $globalImagePullSecrets $schemaImagePullSecrets }} - {{- if $mergedImagePullSecrets }} - imagePullSecrets: - {{- toYaml $mergedImagePullSecrets | nindent 8 }} - {{- end }} - {{- $globalNodeSelector := $.Values.global.nodeSelector | default dict }} - {{- $schemaNodeSelector := $.Values.schema.nodeSelector | default $globalNodeSelector }} - {{- if $schemaNodeSelector }} - nodeSelector: - {{- toYaml $schemaNodeSelector | nindent 8 }} - {{- end }} - {{- $globalAffinity := $.Values.global.affinity | default dict }} - {{- $schemaAffinity := $.Values.schema.affinity | default $globalAffinity }} - {{- if $schemaAffinity }} - affinity: - {{- toYaml $schemaAffinity | nindent 8 }} - {{- end }} - {{- $globalTolerations := $.Values.global.tolerations | default list }} - {{- $schemaTolerations := $.Values.schema.tolerations | default $globalTolerations }} - {{- if $schemaTolerations }} - tolerations: - {{- toYaml $schemaTolerations | nindent 8 }} - {{- end }} - restartPolicy: OnFailure - containers: - - name: cadence-schema-setup - {{- $globalImage := .Values.global.image | default dict }} - {{- $schemaImage := $.Values.frontend.image | default dict }} - {{- $repository := $schemaImage.repository | default $globalImage.repository }} - {{- $tag := $schemaImage.tag | default $globalImage.tag }} - image: {{ $repository }}:{{ $tag }} - {{- $pullPolicy := $schemaImage.pullPolicy | default $globalImage.pullPolicy | default "IfNotPresent" }} - imagePullPolicy: {{ $pullPolicy }} - {{- if eq $dbDriver "cassandra" }} - # Cassandra Schema Setup - command: - - sh - - -c - - | - set -e - echo "Starting Cadence schema setup for driver: $DB_DRIVER" - echo "=== Setting up Cassandra Schema ===" - - # Build cassandra-tool command with TLS options - build_cassandra_cmd() { - local cmd="cadence-cassandra-tool --ep $DB_HOST" - - # Add authentication - if [ -n "$DB_USER" ]; then - cmd="$cmd -u $DB_USER" - fi - if [ -n "$CASSANDRA_PASSWORD" ]; then - cmd="$cmd -pw $CASSANDRA_PASSWORD" - fi - - # Add allowed authenticators from environment variable - if [ -n "$ALLOWED_AUTHENTICATORS" ]; then - cmd="$cmd $ALLOWED_AUTHENTICATORS" - fi - - # Add TLS options if enabled - if [ "$TLS_ENABLED" = "true" ]; then - cmd="$cmd --tls" - if [ -n "$SSL_CERTFILE" ]; then - cmd="$cmd --tls-ca-file $SSL_CERTFILE" - fi - if [ -n "$SSL_CLIENT_CERT" ]; then - cmd="$cmd --tls-cert-file $SSL_CLIENT_CERT" - fi - if [ -n "$SSL_CLIENT_KEY" ]; then - cmd="$cmd --tls-key-file $SSL_CLIENT_KEY" - fi - fi - - echo "$cmd" - } - - # Setup main database schema - echo "Creating main keyspace: $DB_NAME" - $(build_cassandra_cmd) create -k $DB_NAME --rf $REPLICATION_FACTOR - - echo "Setting up main schema version 0.0" - $(build_cassandra_cmd) -k $DB_NAME setup-schema -v 0.0 - - echo "Updating main schema to latest version" - $(build_cassandra_cmd) -k $DB_NAME update-schema -d $CADENCE_HOME/schema/cassandra/cadence/versioned - - # Setup visibility database schema (only if ES is not enabled) - if [ "$ES_ENABLED" = "false" ]; then - echo "Creating visibility keyspace: $DB_VISIBILITY_NAME" - $(build_cassandra_cmd) create -k $DB_VISIBILITY_NAME --rf $REPLICATION_FACTOR - - echo "Setting up visibility schema version 0.0" - $(build_cassandra_cmd) -k $DB_VISIBILITY_NAME setup-schema -v 0.0 - - echo "Updating visibility schema to latest version" - $(build_cassandra_cmd) -k $DB_VISIBILITY_NAME update-schema -d $CADENCE_HOME/schema/cassandra/visibility/versioned - else - echo "Skipping visibility schema setup (Elasticsearch enabled)" - fi - - echo "Schema setup completed successfully!" - env: - # Common environment variables - - name: DB_DRIVER - value: {{ $dbDriver | quote }} - - name: CADENCE_HOME - value: "/etc/cadence" - - name: ES_ENABLED - value: {{ .Values.config.persistence.elasticsearch.enabled | quote }} - # Cassandra specific environment variables - - name: DB_HOST - value: {{ .Values.config.persistence.database.cassandra.hosts | quote }} - - name: DB_NAME - value: {{ .Values.config.persistence.database.cassandra.keyspace | quote }} - - name: DB_VISIBILITY_NAME - value: {{ .Values.config.persistence.database.cassandra.visibilityKeyspace | quote }} - - name: REPLICATION_FACTOR - value: {{ .Values.config.persistence.database.cassandra.replicationFactor | default 1 | quote }} - # Allowed authenticators (build --aa parameters) - - name: ALLOWED_AUTHENTICATORS - value: {{ if .Values.config.persistence.database.cassandra.allowedAuthenticators }}{{ range $index, $auth := .Values.config.persistence.database.cassandra.allowedAuthenticators }}{{ if $index }} {{ end }}--aa {{ $auth | quote }}{{ end }}{{ else }}"--aa org.apache.cassandra.auth.PasswordAuthenticator"{{ end }} - # Authentication parameters (conditional) - {{- if .Values.config.persistence.database.cassandra.user }} - - name: DB_USER - value: {{ .Values.config.persistence.database.cassandra.user | quote }} - {{- end }} - {{- if .Values.config.persistence.database.cassandra.password }} - - name: CASSANDRA_PASSWORD - valueFrom: - secretKeyRef: - name: {{ include "cadence.fullname" $ }}-frontend-secrets - key: CASSANDRA_PASSWORD - {{- end }} - # TLS Configuration - - name: TLS_ENABLED - value: {{ .Values.config.persistence.database.cassandra.tls.enabled | quote }} - {{- if .Values.config.persistence.database.cassandra.tls.enabled }} - # SSL_CERTFILE environment variable (CA certificate) - {{- if .Values.config.persistence.database.cassandra.tls.caFile }} - - name: SSL_CERTFILE - value: {{ .Values.config.persistence.database.cassandra.tls.caFile | quote }} - {{- else if .Values.config.persistence.database.cassandra.tls.caFiles }} - - name: SSL_CERTFILE - value: {{ index .Values.config.persistence.database.cassandra.tls.caFiles 0 | quote }} - {{- end }} - # Client certificate for mutual TLS - {{- if .Values.config.persistence.database.cassandra.tls.certFile }} - - name: SSL_CLIENT_CERT - value: {{ .Values.config.persistence.database.cassandra.tls.certFile | quote }} - {{- end }} - # Client private key for mutual TLS - {{- if .Values.config.persistence.database.cassandra.tls.keyFile }} - - name: SSL_CLIENT_KEY - value: {{ .Values.config.persistence.database.cassandra.tls.keyFile | quote }} - {{- end }} - {{- end }} - - {{- else if eq $dbDriver "postgres" }} - # PostgreSQL Schema Setup - command: - - sh - - -c - - | - set -e - echo "Starting Cadence schema setup for driver: $DB_DRIVER" - echo "=== Setting up PostgreSQL Schema ===" - - # Build sql-tool command with TLS options - build_postgres_cmd() { - local cmd="cadence-sql-tool --ep $DB_HOST -p $DB_PORT -u $DB_USER -pw $POSTGRES_PWD --plugin postgres" - - # Add TLS options if enabled - if [ "$TLS_ENABLED" = "true" ]; then - cmd="$cmd --tls" - if [ -n "$SSL_CERTFILE" ]; then - cmd="$cmd --tls-ca-file $SSL_CERTFILE" - fi - if [ -n "$SSL_CLIENT_CERT" ]; then - cmd="$cmd --tls-cert-file $SSL_CLIENT_CERT" - fi - if [ -n "$SSL_CLIENT_KEY" ]; then - cmd="$cmd --tls-key-file $SSL_CLIENT_KEY" - fi - fi - - echo "$cmd" - } - - # Create main database - echo "Creating main database: $DB_NAME" - $(build_postgres_cmd) create-database --db $DB_NAME - - echo "Setting up main schema version 0.0" - $(build_postgres_cmd) --db $DB_NAME setup-schema -v 0.0 - - echo "Updating main schema to latest version" - $(build_postgres_cmd) --db $DB_NAME update-schema -d $CADENCE_HOME/schema/postgres/cadence/versioned - - # Setup visibility database (only if ES is not enabled) - if [ "$ES_ENABLED" = "false" ]; then - echo "Creating visibility database: $DB_VISIBILITY_NAME" - $(build_postgres_cmd) create-database --db $DB_VISIBILITY_NAME - - echo "Setting up visibility schema version 0.0" - $(build_postgres_cmd) --db $DB_VISIBILITY_NAME setup-schema -v 0.0 - - echo "Updating visibility schema to latest version" - $(build_postgres_cmd) --db $DB_VISIBILITY_NAME update-schema -d $CADENCE_HOME/schema/postgres/visibility/versioned - else - echo "Skipping visibility schema setup (Elasticsearch enabled)" - fi - - echo "Schema setup completed successfully!" - env: - # Common environment variables - - name: DB_DRIVER - value: {{ $dbDriver | quote }} - - name: CADENCE_HOME - value: "/etc/cadence" - - name: ES_ENABLED - value: {{ .Values.config.persistence.elasticsearch.enabled | quote }} - # PostgreSQL specific environment variables - - name: DB_HOST - value: {{ .Values.config.persistence.database.sql.hosts | quote }} - - name: DB_PORT - value: {{ .Values.config.persistence.database.postgres.port | default .Values.config.persistence.database.sql.port | default 5432 | quote }} - - name: DB_NAME - value: {{ .Values.config.persistence.database.sql.dbname | quote }} - - name: DB_VISIBILITY_NAME - value: {{ .Values.config.persistence.database.sql.visibilityDbname | quote }} - - name: DB_USER - value: {{ .Values.config.persistence.database.sql.user | quote }} - # Authentication parameters - - name: POSTGRES_PWD - valueFrom: - secretKeyRef: - name: {{ include "cadence.fullname" $ }}-frontend-secrets - key: POSTGRES_PWD - # TLS Configuration - - name: TLS_ENABLED - value: {{ .Values.config.persistence.database.sql.tls.enabled | quote }} - {{- if .Values.config.persistence.database.sql.tls.enabled }} - # SSL_CERTFILE environment variable (CA certificate) - {{- if .Values.config.persistence.database.sql.tls.caFile }} - - name: SSL_CERTFILE - value: {{ .Values.config.persistence.database.sql.tls.caFile | quote }} - {{- else if .Values.config.persistence.database.sql.tls.caFiles }} - - name: SSL_CERTFILE - value: {{ index .Values.config.persistence.database.sql.tls.caFiles 0 | quote }} - {{- end }} - # Client certificate for mutual TLS - {{- if .Values.config.persistence.database.sql.tls.certFile }} - - name: SSL_CLIENT_CERT - value: {{ .Values.config.persistence.database.sql.tls.certFile | quote }} - {{- end }} - # Client private key for mutual TLS - {{- if .Values.config.persistence.database.sql.tls.keyFile }} - - name: SSL_CLIENT_KEY - value: {{ .Values.config.persistence.database.sql.tls.keyFile | quote }} - {{- end }} - {{- end }} - - {{- else if eq $dbDriver "mysql" }} - # MySQL Schema Setup - command: - - sh - - -c - - | - set -e - echo "Starting Cadence schema setup for driver: $DB_DRIVER" - echo "=== Setting up MySQL Schema ===" - - # Build sql-tool command with TLS options - build_mysql_cmd() { - local cmd="cadence-sql-tool --ep $DB_HOST -p $DB_PORT -u $DB_USER -pw $MYSQL_PWD --plugin mysql" - - # Add TLS options if enabled - if [ "$TLS_ENABLED" = "true" ]; then - cmd="$cmd --tls" - if [ -n "$SSL_CERTFILE" ]; then - cmd="$cmd --tls-ca-file $SSL_CERTFILE" - fi - if [ -n "$SSL_CLIENT_CERT" ]; then - cmd="$cmd --tls-cert-file $SSL_CLIENT_CERT" - fi - if [ -n "$SSL_CLIENT_KEY" ]; then - cmd="$cmd --tls-key-file $SSL_CLIENT_KEY" - fi - fi - - echo "$cmd" - } - - # Create main database - echo "Creating main database: $DB_NAME" - $(build_mysql_cmd) create-database --db $DB_NAME - - echo "Setting up main schema version 0.0" - $(build_mysql_cmd) --db $DB_NAME setup-schema -v 0.0 - - echo "Updating main schema to latest version" - $(build_mysql_cmd) --db $DB_NAME update-schema -d $CADENCE_HOME/schema/mysql/v8/cadence/versioned - - # Setup visibility database (only if ES is not enabled) - if [ "$ES_ENABLED" = "false" ]; then - echo "Creating visibility database: $DB_VISIBILITY_NAME" - $(build_mysql_cmd) create-database --db $DB_VISIBILITY_NAME - - echo "Setting up visibility schema version 0.0" - $(build_mysql_cmd) --db $DB_VISIBILITY_NAME setup-schema -v 0.0 - - echo "Updating visibility schema to latest version" - $(build_mysql_cmd) --db $DB_VISIBILITY_NAME update-schema -d $CADENCE_HOME/schema/mysql/v8/visibility/versioned - else - echo "Skipping visibility schema setup (Elasticsearch enabled)" - fi - - echo "Schema setup completed successfully!" - env: - # Common environment variables - - name: DB_DRIVER - value: {{ $dbDriver | quote }} - - name: CADENCE_HOME - value: "/etc/cadence" - - name: ES_ENABLED - value: {{ .Values.config.persistence.elasticsearch.enabled | quote }} - # MySQL specific environment variables - - name: DB_HOST - value: {{ .Values.config.persistence.database.sql.hosts | quote }} - - name: DB_PORT - value: {{ .Values.config.persistence.database.mysql.port | default .Values.config.persistence.database.sql.port | default 3306 | quote }} - - name: DB_NAME - value: {{ .Values.config.persistence.database.sql.dbname | quote }} - - name: DB_VISIBILITY_NAME - value: {{ .Values.config.persistence.database.sql.visibilityDbname | quote }} - - name: DB_USER - value: {{ .Values.config.persistence.database.sql.user | quote }} - # Authentication parameters - - name: MYSQL_PWD - valueFrom: - secretKeyRef: - name: {{ include "cadence.fullname" $ }}-frontend-secrets - key: MYSQL_PWD - # TLS Configuration - - name: TLS_ENABLED - value: {{ .Values.config.persistence.database.sql.tls.enabled | quote }} - {{- if .Values.config.persistence.database.sql.tls.enabled }} - # SSL_CERTFILE environment variable (CA certificate) - {{- if .Values.config.persistence.database.sql.tls.caFile }} - - name: SSL_CERTFILE - value: {{ .Values.config.persistence.database.sql.tls.caFile | quote }} - {{- else if .Values.config.persistence.database.sql.tls.caFiles }} - - name: SSL_CERTFILE - value: {{ index .Values.config.persistence.database.sql.tls.caFiles 0 | quote }} - {{- end }} - # Client certificate for mutual TLS - {{- if .Values.config.persistence.database.sql.tls.certFile }} - - name: SSL_CLIENT_CERT - value: {{ .Values.config.persistence.database.sql.tls.certFile | quote }} - {{- end }} - # Client private key for mutual TLS - {{- if .Values.config.persistence.database.sql.tls.keyFile }} - - name: SSL_CLIENT_KEY - value: {{ .Values.config.persistence.database.sql.tls.keyFile | quote }} - {{- end }} - {{- end }} - {{- end }} - volumeMounts: - {{- with .Values.global.tls.volumeMounts }} - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.schema.resources }} - resources: - {{- toYaml . | nindent 10 }} - {{- end }} - volumes: - {{- with .Values.global.tls.volumes }} - {{- toYaml . | nindent 6 }} - {{- end }} -{{- end }} \ No newline at end of file diff --git a/charts/cadence/templates/schema-update-job.yaml b/charts/cadence/templates/schema-update-job.yaml deleted file mode 100644 index 8db53bb..0000000 --- a/charts/cadence/templates/schema-update-job.yaml +++ /dev/null @@ -1,407 +0,0 @@ -{{- if .Values.schema.updateJob.enabled -}} -{{- $dbDriver := .Values.config.persistence.database.driver }} ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: {{ include "cadence.fullname" . }}-schema-update - labels: - {{- include "cadence.labels" . | nindent 4 }} - app.kubernetes.io/component: schema-update - annotations: - "helm.sh/hook": pre-install,pre-upgrade - "helm.sh/hook-weight": "-3" - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded -spec: - template: - metadata: - labels: - {{- include "cadence.labels" . | nindent 8 }} - app.kubernetes.io/component: schema-setup - spec: - {{- if $.Values.serviceAccount.create }} - serviceAccountName: {{ include "cadence.serviceAccountName" $ }} - {{- end }} - {{- $globalImagePullSecrets := $.Values.global.imagePullSecrets | default list }} - {{- $schemaImagePullSecrets := $.Values.frontend.imagePullSecrets | default list }} - {{- $mergedImagePullSecrets := concat $globalImagePullSecrets $schemaImagePullSecrets }} - {{- if $mergedImagePullSecrets }} - imagePullSecrets: - {{- toYaml $mergedImagePullSecrets | nindent 8 }} - {{- end }} - {{- $globalNodeSelector := $.Values.global.nodeSelector | default dict }} - {{- $schemaNodeSelector := $.Values.schema.nodeSelector | default $globalNodeSelector }} - {{- if $schemaNodeSelector }} - nodeSelector: - {{- toYaml $schemaNodeSelector | nindent 8 }} - {{- end }} - {{- $globalAffinity := $.Values.global.affinity | default dict }} - {{- $schemaAffinity := $.Values.schema.affinity | default $globalAffinity }} - {{- if $schemaAffinity }} - affinity: - {{- toYaml $schemaAffinity | nindent 8 }} - {{- end }} - {{- $globalTolerations := $.Values.global.tolerations | default list }} - {{- $schemaTolerations := $.Values.schema.tolerations | default $globalTolerations }} - {{- if $schemaTolerations }} - tolerations: - {{- toYaml $schemaTolerations | nindent 8 }} - {{- end }} - restartPolicy: OnFailure - containers: - - name: cadence-schema-update - {{- $globalImage := .Values.global.image | default dict }} - {{- $schemaImage := $.Values.frontend.image | default dict }} - {{- $repository := $schemaImage.repository | default $globalImage.repository }} - {{- $tag := $schemaImage.tag | default $globalImage.tag }} - image: {{ $repository }}:{{ $tag }} - {{- $pullPolicy := $schemaImage.pullPolicy | default $globalImage.pullPolicy | default "IfNotPresent" }} - imagePullPolicy: {{ $pullPolicy }} - {{- if eq $dbDriver "cassandra" }} - # Cassandra Schema Setup - command: - - sh - - -c - - | - set -e - echo "Starting Cadence schema setup for driver: $DB_DRIVER" - echo "=== Setting up Cassandra Schema ===" - - # Build cassandra-tool command with TLS options - build_cassandra_cmd() { - local cmd="cadence-cassandra-tool --ep $DB_HOST" - - # Add authentication - if [ -n "$DB_USER" ]; then - cmd="$cmd -u $DB_USER" - fi - if [ -n "$CASSANDRA_PASSWORD" ]; then - cmd="$cmd -pw $CASSANDRA_PASSWORD" - fi - - # Add allowed authenticators from environment variable - if [ -n "$ALLOWED_AUTHENTICATORS" ]; then - cmd="$cmd $ALLOWED_AUTHENTICATORS" - fi - - # Add TLS options if enabled - if [ "$TLS_ENABLED" = "true" ]; then - cmd="$cmd --tls" - if [ -n "$SSL_CERTFILE" ]; then - cmd="$cmd --tls-ca-file $SSL_CERTFILE" - fi - if [ -n "$SSL_CLIENT_CERT" ]; then - cmd="$cmd --tls-cert-file $SSL_CLIENT_CERT" - fi - if [ -n "$SSL_CLIENT_KEY" ]; then - cmd="$cmd --tls-key-file $SSL_CLIENT_KEY" - fi - fi - - echo "$cmd" - } - - # Setup main database schema - echo "Creating main keyspace: $DB_NAME" - $(build_cassandra_cmd) create -k $DB_NAME --rf $REPLICATION_FACTOR - - echo "Setting up main schema version 0.0" - $(build_cassandra_cmd) -k $DB_NAME setup-schema -v 0.0 - - echo "Updating main schema to latest version" - $(build_cassandra_cmd) -k $DB_NAME update-schema -d $CADENCE_HOME/schema/cassandra/cadence/versioned - - # Setup visibility database schema (only if ES is not enabled) - if [ "$ES_ENABLED" = "false" ]; then - echo "Creating visibility keyspace: $DB_VISIBILITY_NAME" - $(build_cassandra_cmd) create -k $DB_VISIBILITY_NAME --rf $REPLICATION_FACTOR - - echo "Setting up visibility schema version 0.0" - $(build_cassandra_cmd) -k $DB_VISIBILITY_NAME setup-schema -v 0.0 - - echo "Updating visibility schema to latest version" - $(build_cassandra_cmd) -k $DB_VISIBILITY_NAME update-schema -d $CADENCE_HOME/schema/cassandra/visibility/versioned - else - echo "Skipping visibility schema setup (Elasticsearch enabled)" - fi - - echo "Schema setup completed successfully!" - env: - # Common environment variables - - name: DB_DRIVER - value: {{ $dbDriver | quote }} - - name: CADENCE_HOME - value: "/etc/cadence" - - name: ES_ENABLED - value: {{ .Values.config.persistence.elasticsearch.enabled | quote }} - # Cassandra specific environment variables - - name: DB_HOST - value: {{ .Values.config.persistence.database.cassandra.hosts | quote }} - - name: DB_NAME - value: {{ .Values.config.persistence.database.cassandra.keyspace | quote }} - - name: DB_VISIBILITY_NAME - value: {{ .Values.config.persistence.database.cassandra.visibilityKeyspace | quote }} - - name: REPLICATION_FACTOR - value: {{ .Values.config.persistence.database.cassandra.replicationFactor | default 1 | quote }} - # Allowed authenticators (build --aa parameters) - - name: ALLOWED_AUTHENTICATORS - value: {{ if .Values.config.persistence.database.cassandra.allowedAuthenticators }}{{ range $index, $auth := .Values.config.persistence.database.cassandra.allowedAuthenticators }}{{ if $index }} {{ end }}--aa {{ $auth | quote }}{{ end }}{{ else }}"--aa org.apache.cassandra.auth.PasswordAuthenticator"{{ end }} - # Authentication parameters (conditional) - {{- if .Values.config.persistence.database.cassandra.user }} - - name: DB_USER - value: {{ .Values.config.persistence.database.cassandra.user | quote }} - {{- end }} - {{- if .Values.config.persistence.database.cassandra.password }} - - name: CASSANDRA_PASSWORD - valueFrom: - secretKeyRef: - name: {{ include "cadence.fullname" $ }}-frontend-secrets - key: CASSANDRA_PASSWORD - {{- end }} - # TLS Configuration - - name: TLS_ENABLED - value: {{ .Values.config.persistence.database.cassandra.tls.enabled | quote }} - {{- if .Values.config.persistence.database.cassandra.tls.enabled }} - # SSL_CERTFILE environment variable (CA certificate) - {{- if .Values.config.persistence.database.cassandra.tls.caFile }} - - name: SSL_CERTFILE - value: {{ .Values.config.persistence.database.cassandra.tls.caFile | quote }} - {{- else if .Values.config.persistence.database.cassandra.tls.caFiles }} - - name: SSL_CERTFILE - value: {{ index .Values.config.persistence.database.cassandra.tls.caFiles 0 | quote }} - {{- end }} - # Client certificate for mutual TLS - {{- if .Values.config.persistence.database.cassandra.tls.certFile }} - - name: SSL_CLIENT_CERT - value: {{ .Values.config.persistence.database.cassandra.tls.certFile | quote }} - {{- end }} - # Client private key for mutual TLS - {{- if .Values.config.persistence.database.cassandra.tls.keyFile }} - - name: SSL_CLIENT_KEY - value: {{ .Values.config.persistence.database.cassandra.tls.keyFile | quote }} - {{- end }} - {{- end }} - - {{- else if eq $dbDriver "postgres" }} - # PostgreSQL Schema Setup - command: - - sh - - -c - - | - set -e - echo "Starting Cadence schema setup for driver: $DB_DRIVER" - echo "=== Setting up PostgreSQL Schema ===" - - # Build sql-tool command with TLS options - build_postgres_cmd() { - local cmd="cadence-sql-tool --ep $DB_HOST -p $DB_PORT -u $DB_USER -pw $POSTGRES_PWD --plugin postgres" - - # Add TLS options if enabled - if [ "$TLS_ENABLED" = "true" ]; then - cmd="$cmd --tls" - if [ -n "$SSL_CERTFILE" ]; then - cmd="$cmd --tls-ca-file $SSL_CERTFILE" - fi - if [ -n "$SSL_CLIENT_CERT" ]; then - cmd="$cmd --tls-cert-file $SSL_CLIENT_CERT" - fi - if [ -n "$SSL_CLIENT_KEY" ]; then - cmd="$cmd --tls-key-file $SSL_CLIENT_KEY" - fi - fi - - echo "$cmd" - } - - # Create main database - echo "Creating main database: $DB_NAME" - $(build_postgres_cmd) create-database --db $DB_NAME - - echo "Setting up main schema version 0.0" - $(build_postgres_cmd) --db $DB_NAME setup-schema -v 0.0 - - echo "Updating main schema to latest version" - $(build_postgres_cmd) --db $DB_NAME update-schema -d $CADENCE_HOME/schema/postgres/cadence/versioned - - # Setup visibility database (only if ES is not enabled) - if [ "$ES_ENABLED" = "false" ]; then - echo "Creating visibility database: $DB_VISIBILITY_NAME" - $(build_postgres_cmd) create-database --db $DB_VISIBILITY_NAME - - echo "Setting up visibility schema version 0.0" - $(build_postgres_cmd) --db $DB_VISIBILITY_NAME setup-schema -v 0.0 - - echo "Updating visibility schema to latest version" - $(build_postgres_cmd) --db $DB_VISIBILITY_NAME update-schema -d $CADENCE_HOME/schema/postgres/visibility/versioned - else - echo "Skipping visibility schema setup (Elasticsearch enabled)" - fi - - echo "Schema setup completed successfully!" - env: - # Common environment variables - - name: DB_DRIVER - value: {{ $dbDriver | quote }} - - name: CADENCE_HOME - value: "/etc/cadence" - - name: ES_ENABLED - value: {{ .Values.config.persistence.elasticsearch.enabled | quote }} - # PostgreSQL specific environment variables - - name: DB_HOST - value: {{ .Values.config.persistence.database.sql.hosts | quote }} - - name: DB_PORT - value: {{ .Values.config.persistence.database.postgres.port | default .Values.config.persistence.database.sql.port | default 5432 | quote }} - - name: DB_NAME - value: {{ .Values.config.persistence.database.sql.dbname | quote }} - - name: DB_VISIBILITY_NAME - value: {{ .Values.config.persistence.database.sql.visibilityDbname | quote }} - - name: DB_USER - value: {{ .Values.config.persistence.database.sql.user | quote }} - # Authentication parameters - - name: POSTGRES_PWD - valueFrom: - secretKeyRef: - name: {{ include "cadence.fullname" $ }}-frontend-secrets - key: POSTGRES_PWD - # TLS Configuration - - name: TLS_ENABLED - value: {{ .Values.config.persistence.database.sql.tls.enabled | quote }} - {{- if .Values.config.persistence.database.sql.tls.enabled }} - # SSL_CERTFILE environment variable (CA certificate) - {{- if .Values.config.persistence.database.sql.tls.caFile }} - - name: SSL_CERTFILE - value: {{ .Values.config.persistence.database.sql.tls.caFile | quote }} - {{- else if .Values.config.persistence.database.sql.tls.caFiles }} - - name: SSL_CERTFILE - value: {{ index .Values.config.persistence.database.sql.tls.caFiles 0 | quote }} - {{- end }} - # Client certificate for mutual TLS - {{- if .Values.config.persistence.database.sql.tls.certFile }} - - name: SSL_CLIENT_CERT - value: {{ .Values.config.persistence.database.sql.tls.certFile | quote }} - {{- end }} - # Client private key for mutual TLS - {{- if .Values.config.persistence.database.sql.tls.keyFile }} - - name: SSL_CLIENT_KEY - value: {{ .Values.config.persistence.database.sql.tls.keyFile | quote }} - {{- end }} - {{- end }} - - {{- else if eq $dbDriver "mysql" }} - # MySQL Schema Setup - command: - - sh - - -c - - | - set -e - echo "Starting Cadence schema setup for driver: $DB_DRIVER" - echo "=== Setting up MySQL Schema ===" - - # Build sql-tool command with TLS options - build_mysql_cmd() { - local cmd="cadence-sql-tool --ep $DB_HOST -p $DB_PORT -u $DB_USER -pw $MYSQL_PWD --plugin mysql" - - # Add TLS options if enabled - if [ "$TLS_ENABLED" = "true" ]; then - cmd="$cmd --tls" - if [ -n "$SSL_CERTFILE" ]; then - cmd="$cmd --tls-ca-file $SSL_CERTFILE" - fi - if [ -n "$SSL_CLIENT_CERT" ]; then - cmd="$cmd --tls-cert-file $SSL_CLIENT_CERT" - fi - if [ -n "$SSL_CLIENT_KEY" ]; then - cmd="$cmd --tls-key-file $SSL_CLIENT_KEY" - fi - fi - - echo "$cmd" - } - - # Create main database - echo "Creating main database: $DB_NAME" - $(build_mysql_cmd) create-database --db $DB_NAME - - echo "Setting up main schema version 0.0" - $(build_mysql_cmd) --db $DB_NAME setup-schema -v 0.0 - - echo "Updating main schema to latest version" - $(build_mysql_cmd) --db $DB_NAME update-schema -d $CADENCE_HOME/schema/mysql/v8/cadence/versioned - - # Setup visibility database (only if ES is not enabled) - if [ "$ES_ENABLED" = "false" ]; then - echo "Creating visibility database: $DB_VISIBILITY_NAME" - $(build_mysql_cmd) create-database --db $DB_VISIBILITY_NAME - - echo "Setting up visibility schema version 0.0" - $(build_mysql_cmd) --db $DB_VISIBILITY_NAME setup-schema -v 0.0 - - echo "Updating visibility schema to latest version" - $(build_mysql_cmd) --db $DB_VISIBILITY_NAME update-schema -d $CADENCE_HOME/schema/mysql/v8/visibility/versioned - else - echo "Skipping visibility schema setup (Elasticsearch enabled)" - fi - - echo "Schema setup completed successfully!" - env: - # Common environment variables - - name: DB_DRIVER - value: {{ $dbDriver | quote }} - - name: CADENCE_HOME - value: "/etc/cadence" - - name: ES_ENABLED - value: {{ .Values.config.persistence.elasticsearch.enabled | quote }} - # MySQL specific environment variables - - name: DB_HOST - value: {{ .Values.config.persistence.database.sql.hosts | quote }} - - name: DB_PORT - value: {{ .Values.config.persistence.database.mysql.port | default .Values.config.persistence.database.sql.port | default 3306 | quote }} - - name: DB_NAME - value: {{ .Values.config.persistence.database.sql.dbname | quote }} - - name: DB_VISIBILITY_NAME - value: {{ .Values.config.persistence.database.sql.visibilityDbname | quote }} - - name: DB_USER - value: {{ .Values.config.persistence.database.sql.user | quote }} - # Authentication parameters - - name: MYSQL_PWD - valueFrom: - secretKeyRef: - name: {{ include "cadence.fullname" $ }}-frontend-secrets - key: MYSQL_PWD - # TLS Configuration - - name: TLS_ENABLED - value: {{ .Values.config.persistence.database.sql.tls.enabled | quote }} - {{- if .Values.config.persistence.database.sql.tls.enabled }} - # SSL_CERTFILE environment variable (CA certificate) - {{- if .Values.config.persistence.database.sql.tls.caFile }} - - name: SSL_CERTFILE - value: {{ .Values.config.persistence.database.sql.tls.caFile | quote }} - {{- else if .Values.config.persistence.database.sql.tls.caFiles }} - - name: SSL_CERTFILE - value: {{ index .Values.config.persistence.database.sql.tls.caFiles 0 | quote }} - {{- end }} - # Client certificate for mutual TLS - {{- if .Values.config.persistence.database.sql.tls.certFile }} - - name: SSL_CLIENT_CERT - value: {{ .Values.config.persistence.database.sql.tls.certFile | quote }} - {{- end }} - # Client private key for mutual TLS - {{- if .Values.config.persistence.database.sql.tls.keyFile }} - - name: SSL_CLIENT_KEY - value: {{ .Values.config.persistence.database.sql.tls.keyFile | quote }} - {{- end }} - {{- end }} - {{- end }} - volumeMounts: - {{- with .Values.global.tls.volumeMounts }} - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.schema.resources }} - resources: - {{- toYaml . | nindent 10 }} - {{- end }} - volumes: - {{- with .Values.global.tls.volumes }} - {{- toYaml . | nindent 6 }} - {{- end }} -{{- end }} \ No newline at end of file From 604dd7ec1e0059f049a64cb071951c4207f70549 Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed, 25 Jun 2025 12:35:17 +0200 Subject: [PATCH 07/37] delete old schema job --- charts/cadence/templates/schema-job.yaml | 67 ------------------------ 1 file changed, 67 deletions(-) delete mode 100644 charts/cadence/templates/schema-job.yaml diff --git a/charts/cadence/templates/schema-job.yaml b/charts/cadence/templates/schema-job.yaml deleted file mode 100644 index 6b18ae0..0000000 --- a/charts/cadence/templates/schema-job.yaml +++ /dev/null @@ -1,67 +0,0 @@ -{{- if .Values.schema.setupJob.enabled -}} -apiVersion: batch/v1 -kind: Job -metadata: - name: cadence-schema-setup - labels: - {{- include "cadence.labels" . | nindent 4 }} -spec: - backoffLimit: 4 - template: - metadata: - name: cadence-schema-setup - spec: - restartPolicy: "OnFailure" - initContainers: - # Check Cassandra DNS resolution using busybox - - name: check-cassandra-dns - image: busybox - command: ['sh', '-c', ' - until nslookup {{ include "cassandra.endpoint" . }}; do - echo "Waiting for Cassandra service DNS resolution..."; - sleep 5; - done; - echo "Cassandra service DNS resolved."; - '] - # Check Cassandra readiness using cqlsh - - name: check-cassandra-ready - image: {{ $.Values.schema.checkSchema.cassandra.image.repository }}:{{ $.Values.schema.checkSchema.cassandra.image.tag }} - imagePullPolicy: {{ $.Values.schema.checkSchema.cassandra.image.pullPolicy }} - command: ['sh', '-c', ' - until cqlsh {{ include "cassandra.endpoint" . }} 9042 -e "SHOW VERSION"; do - echo "Waiting for Cassandra to start..."; - sleep 30; - done; - echo "Cassandra is up and running."; - '] - containers: - - name: schema-setup - {{- $globalImage := $.Values.global.image | default dict }} - {{- $serviceImage := $.Values.history.image | default dict }} - {{- $repository := $serviceImage.repository | default $globalImage.repository }} - {{- $tag := $serviceImage.tag | default $globalImage.tag }} - image: {{ $repository }}:{{ $tag }} - imagePullPolicy: Always - env: - - name: CASSANDRA_SEEDS - value: {{ include "cassandra.endpoint" . }} - - name: KEYSPACE - value: cadence - - name: VISIBILITY_KEYSPACE - value: cadence_visibility - - name: RF - value: "1" - - name: CADENCE_HOME - value: /etc/cadence - args: ['sh', '-c', ' - SCHEMA_DIR=$CADENCE_HOME/schema/cassandra/cadence/versioned; - cadence-cassandra-tool --ep $CASSANDRA_SEEDS create -k $KEYSPACE --rf $RF; - cadence-cassandra-tool --ep $CASSANDRA_SEEDS -k $KEYSPACE setup-schema -v 0.0; - cadence-cassandra-tool --ep $CASSANDRA_SEEDS -k $KEYSPACE update-schema -d $SCHEMA_DIR; - - VISIBILITY_SCHEMA_DIR=$CADENCE_HOME/schema/cassandra/visibility/versioned; - cadence-cassandra-tool --ep $CASSANDRA_SEEDS create -k $VISIBILITY_KEYSPACE --rf $RF; - cadence-cassandra-tool --ep $CASSANDRA_SEEDS -k $VISIBILITY_KEYSPACE setup-schema -v 0.0; - cadence-cassandra-tool --ep $CASSANDRA_SEEDS -k $VISIBILITY_KEYSPACE update-schema -d $VISIBILITY_SCHEMA_DIR; - '] -{{- end }} \ No newline at end of file From a85bf35a774d636fdac52e67dc373c6f4e9f3db5 Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed, 25 Jun 2025 12:37:59 +0200 Subject: [PATCH 08/37] Update schema-server-job.yaml --- charts/cadence/templates/schema-server-job.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/charts/cadence/templates/schema-server-job.yaml b/charts/cadence/templates/schema-server-job.yaml index a19a915..8062e23 100644 --- a/charts/cadence/templates/schema-server-job.yaml +++ b/charts/cadence/templates/schema-server-job.yaml @@ -1,4 +1,4 @@ -{{- if .Values.schema.createJob.enabled -}} +{{- if .Values.schema.serverJob.enabled -}} {{- $dbDriver := .Values.config.persistence.database.driver }} --- apiVersion: batch/v1 @@ -10,7 +10,7 @@ metadata: app.kubernetes.io/component: schema-server annotations: "helm.sh/hook": pre-install,pre-upgrade - "helm.sh/hook-weight": "-3" + "helm.sh/hook-weight": "-1" "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded spec: template: @@ -30,19 +30,19 @@ spec: {{- toYaml $mergedImagePullSecrets | nindent 8 }} {{- end }} {{- $globalNodeSelector := $.Values.global.nodeSelector | default dict }} - {{- $schemaNodeSelector := $.Values.schema.nodeSelector | default $globalNodeSelector }} + {{- $schemaNodeSelector := $.Values.schema.serverJob.nodeSelector | default $globalNodeSelector }} {{- if $schemaNodeSelector }} nodeSelector: {{- toYaml $schemaNodeSelector | nindent 8 }} {{- end }} {{- $globalAffinity := $.Values.global.affinity | default dict }} - {{- $schemaAffinity := $.Values.schema.affinity | default $globalAffinity }} + {{- $schemaAffinity := $.Values.schema.serverJob.affinity | default $globalAffinity }} {{- if $schemaAffinity }} affinity: {{- toYaml $schemaAffinity | nindent 8 }} {{- end }} {{- $globalTolerations := $.Values.global.tolerations | default list }} - {{- $schemaTolerations := $.Values.schema.tolerations | default $globalTolerations }} + {{- $schemaTolerations := $.Values.schema.serverJob.tolerations | default $globalTolerations }} {{- if $schemaTolerations }} tolerations: {{- toYaml $schemaTolerations | nindent 8 }} From 2e49d729d2b74f4ad03389f4c35a6a1d031c0eff Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed, 25 Jun 2025 12:39:08 +0200 Subject: [PATCH 09/37] values.schema restructure final --- charts/cadence/values.yaml | 57 +++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/charts/cadence/values.yaml b/charts/cadence/values.yaml index 4569b09..d81e3d8 100644 --- a/charts/cadence/values.yaml +++ b/charts/cadence/values.yaml @@ -1117,28 +1117,47 @@ dynamicConfig: #################### SCHEMA CONFIGURATION ##################### ############################################################### schema: - # -- Schema Jobs resources - resources: {} - # limits: - # cpu: "500m" - # memory: "1Gi" - # requests: - # cpu: "500m" - # memory: "1Gi" - # -- Schema Jobs affinity rules - affinity: {} - # -- Schema Jobs tolerations - tolerations: [] - # -- Schema Jobs node selector - nodeSelector: {} - - createJob: + # This job have validation mechanism, so it wont replace your database if it's already existing. + # It wont rollback your schema version to avoid data deletion + # It will update your schema version if there is any update avaibable. + # All the schema parameters are taken from .Values.config so configure all regarding before running the schema job. + serverJob: enabled: true - setupJob: + + # -- Schema Jobs resources + resources: {} + # limits: + # cpu: "500m" + # memory: "1Gi" + # requests: + # cpu: "500m" + # memory: "1Gi" + # -- Schema Jobs affinity rules + affinity: {} + # -- Schema Jobs tolerations + tolerations: [] + # -- Schema Jobs node selector + nodeSelector: {} + + elasticSearchJob: enabled: true - updateJob: - enabled: false + # -- Schema Jobs resources + resources: {} + # limits: + # cpu: "500m" + # memory: "1Gi" + # requests: + # cpu: "500m" + # memory: "1Gi" + # -- Schema Jobs affinity rules + affinity: {} + # -- Schema Jobs tolerations + tolerations: [] + # -- Schema Jobs node selector + nodeSelector: {} + + # Pending to be deprecated for auto-detection. version: default: "0.42" visibility: "0.9" From 6371e9879a22d0e351cd72b29dd30415582bf25f Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed, 25 Jun 2025 13:02:28 +0200 Subject: [PATCH 10/37] hooks needed --- charts/cadence/templates/server-secret.yaml | 2 ++ charts/cadence/templates/serviceaccount.yaml | 1 + 2 files changed, 3 insertions(+) diff --git a/charts/cadence/templates/server-secret.yaml b/charts/cadence/templates/server-secret.yaml index c8033e9..3ab64d8 100644 --- a/charts/cadence/templates/server-secret.yaml +++ b/charts/cadence/templates/server-secret.yaml @@ -26,6 +26,8 @@ metadata: labels: {{- include "cadence.labels" $ | nindent 4 }} app.kubernetes.io/component: {{ $serviceName }} + annotations: + "helm.sh/hook-weight": "-2" type: Opaque data: {{- range $secret := $mergedSecrets }} diff --git a/charts/cadence/templates/serviceaccount.yaml b/charts/cadence/templates/serviceaccount.yaml index 27841d6..13dcacd 100644 --- a/charts/cadence/templates/serviceaccount.yaml +++ b/charts/cadence/templates/serviceaccount.yaml @@ -8,6 +8,7 @@ metadata: {{- with .Values.serviceAccount.annotations }} annotations: {{- toYaml . | nindent 4 }} + "helm.sh/hook-weight": "-2" {{- end }} automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }} {{- end }} \ No newline at end of file From 4bc9b5375ad77bb74d666ce676b4baefb912db24 Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed, 25 Jun 2025 13:02:46 +0200 Subject: [PATCH 11/37] wait-for-database added --- .../cadence/templates/schema-server-job.yaml | 340 +++++++++++++++++- 1 file changed, 338 insertions(+), 2 deletions(-) diff --git a/charts/cadence/templates/schema-server-job.yaml b/charts/cadence/templates/schema-server-job.yaml index 8062e23..484a46c 100644 --- a/charts/cadence/templates/schema-server-job.yaml +++ b/charts/cadence/templates/schema-server-job.yaml @@ -9,9 +9,8 @@ metadata: {{- include "cadence.labels" . | nindent 4 }} app.kubernetes.io/component: schema-server annotations: - "helm.sh/hook": pre-install,pre-upgrade "helm.sh/hook-weight": "-1" - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + "helm.sh/hook-delete-policy": hook-succeeded spec: template: metadata: @@ -48,6 +47,343 @@ spec: {{- toYaml $schemaTolerations | nindent 8 }} {{- end }} restartPolicy: OnFailure + initContainers: + - name: wait-for-database + {{- if eq $dbDriver "cassandra" }} + image: {{ $.Values.schema.checkSchema.cassandra.image.repository }}:{{ $.Values.schema.checkSchema.cassandra.image.tag }} + imagePullPolicy: {{ $.Values.schema.checkSchema.cassandra.image.pullPolicy }} + command: + - sh + - -c + - | + # Create .cassandra directory for cqlshrc if it doesn't exist + mkdir -p ~/.cassandra + + # Build cqlshrc configuration file + cat > ~/.cassandra/cqlshrc << EOF + [connection] + hostname = $DB_HOST + port = $DB_PORT + + EOF + + # Add authentication section if user is provided + if [ -n "$DB_USER" ] && [ "$DB_USER" != "" ]; then + cat >> ~/.cassandra/cqlshrc << EOF + [authentication] + username = $DB_USER + EOF + # Add password if provided + if [ -n "$CASSANDRA_PASSWORD" ] && [ "$CASSANDRA_PASSWORD" != "" ]; then + cat >> ~/.cassandra/cqlshrc << EOF + password = $CASSANDRA_PASSWORD + EOF + fi + fi + + # Add SSL configuration if enabled + if [ "$TLS_ENABLED" = "true" ]; then + cat >> ~/.cassandra/cqlshrc << EOF + + [ssl] + EOF + # Add certificate file if specified (CA certificate) + if [ -n "$SSL_CERTFILE" ] && [ "$SSL_CERTFILE" != "" ]; then + cat >> ~/.cassandra/cqlshrc << EOF + certfile = $SSL_CERTFILE + EOF + fi + + # Add client certificate for mutual TLS + if [ -n "$SSL_CLIENT_CERT" ] && [ "$SSL_CLIENT_CERT" != "" ]; then + cat >> ~/.cassandra/cqlshrc << EOF + usercert = $SSL_CLIENT_CERT + EOF + fi + + # Add client private key for mutual TLS + if [ -n "$SSL_CLIENT_KEY" ] && [ "$SSL_CLIENT_KEY" != "" ]; then + cat >> ~/.cassandra/cqlshrc << EOF + userkey = $SSL_CLIENT_KEY + EOF + fi + + # Add validate setting + if [ -n "$SSL_VALIDATE" ] && [ "$SSL_VALIDATE" != "" ]; then + cat >> ~/.cassandra/cqlshrc << EOF + validate = $SSL_VALIDATE + EOF + else + cat >> ~/.cassandra/cqlshrc << EOF + validate = true + EOF + fi + fi + + # Debug: Show generated cqlshrc (remove in production) + echo "Generated cqlshrc:" + cat ~/.cassandra/cqlshrc + echo "---" + + # Build cqlsh command + build_cqlsh_cmd() { + local cmd="cqlsh" + + # Add SSL option if enabled + if [ "$TLS_ENABLED" = "true" ]; then + cmd="$cmd --ssl" + fi + + echo "$cmd" + } + + # Compare database version with cadence schema version + until $(build_cqlsh_cmd) -e " + SELECT now() FROM system.local;" > /dev/null 2>&1 + do + echo 'Waiting for Cassandra to be ready...' + sleep 5 + done + echo "Cassandra is ready!" + env: + - name: ES_ENABLED + value: {{ $.Values.config.persistence.elasticsearch.enabled | quote }} + # Basic connection parameters + - name: DB_HOST + value: {{ $.Values.config.persistence.database.cassandra.hosts | quote }} + - name: DB_PORT + value: {{ $.Values.config.persistence.database.cassandra.port | quote }} + - name: DB_NAME + value: {{ $.Values.config.persistence.database.cassandra.keyspace | quote }} + - name: DB_VISIBILITY_NAME + value: {{ $.Values.config.persistence.database.cassandra.visibilityKeyspace | quote }} + # Authentication parameters (conditional) + {{- if $.Values.config.persistence.database.cassandra.user }} + - name: DB_USER + value: {{ $.Values.config.persistence.database.cassandra.user | quote }} + {{- end }} + {{- if $.Values.config.persistence.database.cassandra.password }} + - name: CASSANDRA_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "cadence.fullname" $ }}-frontend-secrets + key: CASSANDRA_PASSWORD + {{- end }} + # TLS Configuration + - name: TLS_ENABLED + value: {{ $.Values.config.persistence.database.cassandra.tls.enabled | quote }} + {{- if $.Values.config.persistence.database.cassandra.tls.enabled }} + # SSL_CERTFILE environment variable (CA certificate) + {{- if $.Values.config.persistence.database.cassandra.tls.caFile }} + - name: SSL_CERTFILE + value: {{ $.Values.config.persistence.database.cassandra.tls.caFile | quote }} + {{- else if $.Values.config.persistence.database.cassandra.tls.caFiles }} + - name: SSL_CERTFILE + value: {{ index $.Values.config.persistence.database.cassandra.tls.caFiles 0 | quote }} + {{- end }} + # Client certificate for mutual TLS + {{- if $.Values.config.persistence.database.cassandra.tls.certFile }} + - name: SSL_CLIENT_CERT + value: {{ $.Values.config.persistence.database.cassandra.tls.certFile | quote }} + {{- end }} + # Client private key for mutual TLS + {{- if $.Values.config.persistence.database.cassandra.tls.keyFile }} + - name: SSL_CLIENT_KEY + value: {{ $.Values.config.persistence.database.cassandra.tls.keyFile | quote }} + {{- end }} + # SSL_VALIDATE environment variable + - name: SSL_VALIDATE + value: {{ $.Values.config.persistence.database.cassandra.tls.enableHostVerification | quote }} + {{- end }} + {{- else if eq $dbDriver "postgres" }} + image: {{ $.Values.schema.checkSchema.postgres.image.repository }}:{{ $.Values.schema.checkSchema.postgres.image.tag }} + imagePullPolicy: {{ $.Values.schema.checkSchema.postgres.image.pullPolicy }} + command: + - sh + - -c + - | + # Build connection string based on TLS configuration + build_psql_cmd() { + local cmd="psql -h $DB_HOST -p $DB_PORT -U $DB_USER" + + # Add SSL mode if TLS is enabled + if [ "$TLS_ENABLED" = "true" ] && [ -n "$SSL_MODE" ]; then + cmd="$cmd --set=sslmode=$SSL_MODE" + + # Add SSL certificate parameters if provided + if [ -n "$SSL_CERTFILE" ]; then + cmd="$cmd --set=sslrootcert=$SSL_CERTFILE" + fi + + if [ -n "$SSL_CLIENT_CERT" ]; then + cmd="$cmd --set=sslcert=$SSL_CLIENT_CERT" + fi + + if [ -n "$SSL_CLIENT_KEY" ]; then + cmd="$cmd --set=sslkey=$SSL_CLIENT_KEY" + fi + fi + + echo "$cmd" + } + + # Wait for PostgreSQL to be ready + echo "Waiting for PostgreSQL to be ready..." + until pg_isready -h $DB_HOST -p $DB_PORT -U $DB_USER; do + echo 'PostgreSQL is not ready yet...' + sleep 5 + done + echo "PostgreSQL is ready!" + env: + - name: ES_ENABLED + value: {{ $.Values.config.persistence.elasticsearch.enabled | quote }} + # Basic connection parameters + - name: DB_HOST + value: {{ $.Values.config.persistence.database.sql.hosts | quote }} + - name: DB_PORT + value: {{ $.Values.config.persistence.database.postgres.port | default $.Values.config.persistence.database.sql.port | default 5432 | quote }} + - name: DB_NAME + value: {{ $.Values.config.persistence.database.sql.dbname | quote }} + - name: DB_VISIBILITY_NAME + value: {{ $.Values.config.persistence.database.sql.visibilityDbname | quote }} + - name: DB_USER + value: {{ $.Values.config.persistence.database.sql.user | quote }} + # Authentication parameters + - name: POSTGRES_PWD + valueFrom: + secretKeyRef: + name: {{ include "cadence.fullname" $ }}-frontend-secrets + key: POSTGRES_PWD + # TLS Configuration + - name: TLS_ENABLED + value: {{ $.Values.config.persistence.database.sql.tls.enabled | quote }} + {{- if $.Values.config.persistence.database.sql.tls.enabled }} + # SSL Mode + - name: SSL_MODE + value: {{ $.Values.config.persistence.database.sql.tls.sslMode | default "require" | quote }} + # SSL_CERTFILE environment variable (CA certificate) + {{- if $.Values.config.persistence.database.sql.tls.caFile }} + - name: SSL_CERTFILE + value: {{ $.Values.config.persistence.database.sql.tls.caFile | quote }} + {{- else if $.Values.config.persistence.database.sql.tls.caFiles }} + - name: SSL_CERTFILE + value: {{ index $.Values.config.persistence.database.sql.tls.caFiles 0 | quote }} + {{- end }} + # Client certificate for mutual TLS + {{- if $.Values.config.persistence.database.sql.tls.certFile }} + - name: SSL_CLIENT_CERT + value: {{ $.Values.config.persistence.database.sql.tls.certFile | quote }} + {{- end }} + # Client private key for mutual TLS + {{- if $.Values.config.persistence.database.sql.tls.keyFile }} + - name: SSL_CLIENT_KEY + value: {{ $.Values.config.persistence.database.sql.tls.keyFile | quote }} + {{- end }} + {{- end }} + {{- else if eq $dbDriver "mysql" }} + image: {{ $.Values.schema.checkSchema.mysql.image.repository }}:{{ $.Values.schema.checkSchema.mysql.image.tag }} + imagePullPolicy: {{ $.Values.schema.checkSchema.mysql.image.pullPolicy }} + command: + - sh + - -c + - | + # Build connection string based on TLS configuration + build_mysql_cmd() { + local cmd="mariadb -h $DB_HOST -P $DB_PORT -u $DB_USER" + + # Add SSL parameters if TLS is enabled + if [ "$TLS_ENABLED" = "true" ]; then + case "$SSL_MODE" in + "disable"|"false") + cmd="$cmd --skip-ssl" + ;; + "preferred") + ;; + "required"|"true"|"skip-verify") + cmd="$cmd --ssl --ssl-verify-server-cert=false" + ;; + "verify-ca") + cmd="$cmd --ssl --ssl-verify-server-cert" + ;; + "verify-identity") + cmd="$cmd --ssl --ssl-verify-server-cert" + ;; + *) + cmd="$cmd --ssl" + ;; + esac + + # Add SSL certificate parameters if provided + if [ -n "$SSL_CERTFILE" ]; then + cmd="$cmd --ssl-ca=$SSL_CERTFILE" + fi + + if [ -n "$SSL_CLIENT_CERT" ]; then + cmd="$cmd --ssl-cert=$SSL_CLIENT_CERT" + fi + + if [ -n "$SSL_CLIENT_KEY" ]; then + cmd="$cmd --ssl-key=$SSL_CLIENT_KEY" + fi + fi + + echo "$cmd" + } + + # Wait for MySQL to be ready + echo "Waiting for MySQL to be ready..." + until mariadb-admin ping -h $DB_HOST -P $DB_PORT -u $DB_USER --password=$MYSQL_PWD --skip-ssl --silent; do + echo 'MySQL is not ready yet...' + sleep 5 + done + echo "MySQL is ready!" + env: + - name: ES_ENABLED + value: {{ $.Values.config.persistence.elasticsearch.enabled | quote }} + # Basic connection parameters + - name: DB_HOST + value: {{ $.Values.config.persistence.database.sql.hosts | quote }} + - name: DB_PORT + value: {{ $.Values.config.persistence.database.mysql.port | default $.Values.config.persistence.database.sql.port | default 3306 | quote }} + - name: DB_NAME + value: {{ $.Values.config.persistence.database.sql.dbname | quote }} + - name: DB_VISIBILITY_NAME + value: {{ $.Values.config.persistence.database.sql.visibilityDbname | quote }} + - name: DB_USER + value: {{ $.Values.config.persistence.database.sql.user | quote }} + # Authentication parameters + - name: MYSQL_PWD + valueFrom: + secretKeyRef: + name: {{ include "cadence.fullname" $ }}-frontend-secrets + key: MYSQL_PWD + # TLS Configuration + - name: TLS_ENABLED + value: {{ $.Values.config.persistence.database.sql.tls.enabled | quote }} + {{- if $.Values.config.persistence.database.sql.tls.enabled }} + # SSL Mode + - name: SSL_MODE + value: {{ $.Values.config.persistence.database.sql.tls.sslMode | default "require" | quote }} + # SSL_CERTFILE environment variable (CA certificate) + {{- if $.Values.config.persistence.database.sql.tls.caFile }} + - name: SSL_CERTFILE + value: {{ $.Values.config.persistence.database.sql.tls.caFile | quote }} + {{- else if $.Values.config.persistence.database.sql.tls.caFiles }} + - name: SSL_CERTFILE + value: {{ index $.Values.config.persistence.database.sql.tls.caFiles 0 | quote }} + {{- end }} + # Client certificate for mutual TLS + {{- if $.Values.config.persistence.database.sql.tls.certFile }} + - name: SSL_CLIENT_CERT + value: {{ $.Values.config.persistence.database.sql.tls.certFile | quote }} + {{- end }} + # Client private key for mutual TLS + {{- if $.Values.config.persistence.database.sql.tls.keyFile }} + - name: SSL_CLIENT_KEY + value: {{ $.Values.config.persistence.database.sql.tls.keyFile | quote }} + {{- end }} + {{- end }} + {{- end }} containers: - name: cadence-schema-server {{- $globalImage := .Values.global.image | default dict }} From 791fafb84bc5b464af9632a0faf7b4dab5074d14 Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed, 25 Jun 2025 13:27:14 +0200 Subject: [PATCH 12/37] datacenter and setup handler added --- .../cadence/templates/schema-server-job.yaml | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/charts/cadence/templates/schema-server-job.yaml b/charts/cadence/templates/schema-server-job.yaml index 484a46c..53f7998 100644 --- a/charts/cadence/templates/schema-server-job.yaml +++ b/charts/cadence/templates/schema-server-job.yaml @@ -439,10 +439,14 @@ spec: # Setup main database schema echo "Creating main keyspace: $DB_NAME" - $(build_cassandra_cmd) create -k $DB_NAME --rf $REPLICATION_FACTOR - + if [ "$DATA_CENTER" = "" ]; then + $(build_cassandra_cmd) create -k $DB_NAME --rf $REPLICATION_FACTOR + else + $(build_cassandra_cmd) create -k $DB_NAME --rf $REPLICATION_FACTOR -dc $DATA_CENTER + fi + echo "Setting up main schema version 0.0" - $(build_cassandra_cmd) -k $DB_NAME setup-schema -v 0.0 + $(build_cassandra_cmd) -k $DB_NAME setup-schema -v 0.0 || echo "Schema already exists" echo "Updating main schema to latest version" $(build_cassandra_cmd) -k $DB_NAME update-schema -d $CADENCE_HOME/schema/cassandra/cadence/versioned @@ -450,10 +454,14 @@ spec: # Setup visibility database schema (only if ES is not enabled) if [ "$ES_ENABLED" = "false" ]; then echo "Creating visibility keyspace: $DB_VISIBILITY_NAME" - $(build_cassandra_cmd) create -k $DB_VISIBILITY_NAME --rf $REPLICATION_FACTOR - + if [ "$DATA_CENTER" = "" ]; then + $(build_cassandra_cmd) create -k $DB_VISIBILITY_NAME --rf $REPLICATION_FACTOR + else + $(build_cassandra_cmd) create -k $DB_VISIBILITY_NAME --rf $REPLICATION_FACTOR -dc $DATA_CENTER + fi + echo "Setting up visibility schema version 0.0" - $(build_cassandra_cmd) -k $DB_VISIBILITY_NAME setup-schema -v 0.0 + $(build_cassandra_cmd) -k $DB_VISIBILITY_NAME setup-schema -v 0.0 || echo "Schema already exists" echo "Updating visibility schema to latest version" $(build_cassandra_cmd) -k $DB_VISIBILITY_NAME update-schema -d $CADENCE_HOME/schema/cassandra/visibility/versioned @@ -479,6 +487,8 @@ spec: value: {{ .Values.config.persistence.database.cassandra.visibilityKeyspace | quote }} - name: REPLICATION_FACTOR value: {{ .Values.config.persistence.database.cassandra.replicationFactor | default 1 | quote }} + - name: DATA_CENTER + value: {{ .Values.config.persistence.database.cassandra.datacenter | quote }} # Allowed authenticators (build --aa parameters) - name: ALLOWED_AUTHENTICATORS value: {{ if .Values.config.persistence.database.cassandra.allowedAuthenticators }}{{ range $index, $auth := .Values.config.persistence.database.cassandra.allowedAuthenticators }}{{ if $index }} {{ end }}--aa {{ $auth | quote }}{{ end }}{{ else }}"--aa org.apache.cassandra.auth.PasswordAuthenticator"{{ end }} @@ -554,7 +564,7 @@ spec: $(build_postgres_cmd) create-database --db $DB_NAME echo "Setting up main schema version 0.0" - $(build_postgres_cmd) --db $DB_NAME setup-schema -v 0.0 + $(build_postgres_cmd) --db $DB_NAME setup-schema -v 0.0 || echo "Schema already exists" echo "Updating main schema to latest version" $(build_postgres_cmd) --db $DB_NAME update-schema -d $CADENCE_HOME/schema/postgres/cadence/versioned @@ -565,7 +575,7 @@ spec: $(build_postgres_cmd) create-database --db $DB_VISIBILITY_NAME echo "Setting up visibility schema version 0.0" - $(build_postgres_cmd) --db $DB_VISIBILITY_NAME setup-schema -v 0.0 + $(build_postgres_cmd) --db $DB_VISIBILITY_NAME setup-schema -v 0.0 || echo "Schema already exists" echo "Updating visibility schema to latest version" $(build_postgres_cmd) --db $DB_VISIBILITY_NAME update-schema -d $CADENCE_HOME/schema/postgres/visibility/versioned @@ -659,7 +669,7 @@ spec: $(build_mysql_cmd) create-database --db $DB_NAME echo "Setting up main schema version 0.0" - $(build_mysql_cmd) --db $DB_NAME setup-schema -v 0.0 + $(build_mysql_cmd) --db $DB_NAME setup-schema -v 0.0 || echo "Schema already exists" echo "Updating main schema to latest version" $(build_mysql_cmd) --db $DB_NAME update-schema -d $CADENCE_HOME/schema/mysql/v8/cadence/versioned @@ -670,7 +680,7 @@ spec: $(build_mysql_cmd) create-database --db $DB_VISIBILITY_NAME echo "Setting up visibility schema version 0.0" - $(build_mysql_cmd) --db $DB_VISIBILITY_NAME setup-schema -v 0.0 + $(build_mysql_cmd) --db $DB_VISIBILITY_NAME setup-schema -v 0.0 || echo "Schema already exists" echo "Updating visibility schema to latest version" $(build_mysql_cmd) --db $DB_VISIBILITY_NAME update-schema -d $CADENCE_HOME/schema/mysql/v8/visibility/versioned From 144ee31c7b3cbd59f4cb83b18c23d698e9de79b7 Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed, 25 Jun 2025 13:54:25 +0200 Subject: [PATCH 13/37] Revert "hooks needed" This reverts commit 6371e9879a22d0e351cd72b29dd30415582bf25f. --- charts/cadence/templates/server-secret.yaml | 2 -- charts/cadence/templates/serviceaccount.yaml | 1 - 2 files changed, 3 deletions(-) diff --git a/charts/cadence/templates/server-secret.yaml b/charts/cadence/templates/server-secret.yaml index 3ab64d8..c8033e9 100644 --- a/charts/cadence/templates/server-secret.yaml +++ b/charts/cadence/templates/server-secret.yaml @@ -26,8 +26,6 @@ metadata: labels: {{- include "cadence.labels" $ | nindent 4 }} app.kubernetes.io/component: {{ $serviceName }} - annotations: - "helm.sh/hook-weight": "-2" type: Opaque data: {{- range $secret := $mergedSecrets }} diff --git a/charts/cadence/templates/serviceaccount.yaml b/charts/cadence/templates/serviceaccount.yaml index 13dcacd..27841d6 100644 --- a/charts/cadence/templates/serviceaccount.yaml +++ b/charts/cadence/templates/serviceaccount.yaml @@ -8,7 +8,6 @@ metadata: {{- with .Values.serviceAccount.annotations }} annotations: {{- toYaml . | nindent 4 }} - "helm.sh/hook-weight": "-2" {{- end }} automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }} {{- end }} \ No newline at end of file From eaeabf6463dc2b418eeafc31f4884fb4111adac3 Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed, 25 Jun 2025 13:57:05 +0200 Subject: [PATCH 14/37] auto-delete job after success --- charts/cadence/templates/schema-server-job.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/charts/cadence/templates/schema-server-job.yaml b/charts/cadence/templates/schema-server-job.yaml index 53f7998..eab8af7 100644 --- a/charts/cadence/templates/schema-server-job.yaml +++ b/charts/cadence/templates/schema-server-job.yaml @@ -8,10 +8,8 @@ metadata: labels: {{- include "cadence.labels" . | nindent 4 }} app.kubernetes.io/component: schema-server - annotations: - "helm.sh/hook-weight": "-1" - "helm.sh/hook-delete-policy": hook-succeeded spec: + ttlSecondsAfterFinished: 60 template: metadata: labels: From 852cae8608c4ed9c81d54b7739c46dbfdcfebb26 Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed, 25 Jun 2025 14:01:58 +0200 Subject: [PATCH 15/37] add protocol version --- charts/cadence/templates/schema-server-job.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/charts/cadence/templates/schema-server-job.yaml b/charts/cadence/templates/schema-server-job.yaml index eab8af7..3c21e4a 100644 --- a/charts/cadence/templates/schema-server-job.yaml +++ b/charts/cadence/templates/schema-server-job.yaml @@ -413,6 +413,9 @@ spec: cmd="$cmd -pw $CASSANDRA_PASSWORD" fi + # Add protocol version + cmd="$cmd -pv $PROTOCOL_VERSION" + # Add allowed authenticators from environment variable if [ -n "$ALLOWED_AUTHENTICATORS" ]; then cmd="$cmd $ALLOWED_AUTHENTICATORS" @@ -481,6 +484,8 @@ spec: value: {{ .Values.config.persistence.database.cassandra.hosts | quote }} - name: DB_NAME value: {{ .Values.config.persistence.database.cassandra.keyspace | quote }} + - name: PROTOCOL_VERSION + value: {{ .Values.config.persistence.database.cassandra.protoVersion | quote }} - name: DB_VISIBILITY_NAME value: {{ .Values.config.persistence.database.cassandra.visibilityKeyspace | quote }} - name: REPLICATION_FACTOR From 9b640af41ca0e5c35baedfd106b455d95ce231d6 Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed, 25 Jun 2025 14:03:57 +0200 Subject: [PATCH 16/37] remove useless helper --- charts/cadence/templates/_helpers.tpl | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/charts/cadence/templates/_helpers.tpl b/charts/cadence/templates/_helpers.tpl index 1b2230c..9cfd00d 100644 --- a/charts/cadence/templates/_helpers.tpl +++ b/charts/cadence/templates/_helpers.tpl @@ -134,11 +134,4 @@ Cadence GRPC Peers endpoint */}} {{- define "cadence.grpcPeers" -}} {{ include "cadence.fullname" . }}-frontend.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.frontend.grpcPort | default 7833 }} -{{- end }} - -{{/* -Get the Cassandra endpoint -*/}} -{{- define "cassandra.endpoint" -}} -{{ .Values.cassandra.endpoint | default (printf "cassandra-service.%s.svc.cluster.local" $.Release.Namespace) }} -{{- end -}} \ No newline at end of file +{{- end }} \ No newline at end of file From 80015e155ce55169953bd85592651641c9b443b3 Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed, 25 Jun 2025 15:50:53 +0200 Subject: [PATCH 17/37] helm ignore added --- charts/cadence/.helmignore | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 charts/cadence/.helmignore diff --git a/charts/cadence/.helmignore b/charts/cadence/.helmignore new file mode 100644 index 0000000..b1eeb65 --- /dev/null +++ b/charts/cadence/.helmignore @@ -0,0 +1,24 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ +# Cadence +docs/ \ No newline at end of file From 9f326b0a7eb09d25042fa0aee555c78119c2591b Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed, 25 Jun 2025 16:26:45 +0200 Subject: [PATCH 18/37] Delete .helmignore --- charts/cadence/templates/.helmignore | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100644 charts/cadence/templates/.helmignore diff --git a/charts/cadence/templates/.helmignore b/charts/cadence/templates/.helmignore deleted file mode 100644 index 0e8a0eb..0000000 --- a/charts/cadence/templates/.helmignore +++ /dev/null @@ -1,23 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*.orig -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ From 2a28fb63d0250211d824f9c2a652517854f47097 Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed, 25 Jun 2025 16:29:44 +0200 Subject: [PATCH 19/37] wait-frontend added to web --- charts/cadence/templates/web-deployment.yaml | 22 +++++ charts/cadence/values.yaml | 91 +++++++++----------- 2 files changed, 64 insertions(+), 49 deletions(-) diff --git a/charts/cadence/templates/web-deployment.yaml b/charts/cadence/templates/web-deployment.yaml index 2eaad2c..bf1f39d 100644 --- a/charts/cadence/templates/web-deployment.yaml +++ b/charts/cadence/templates/web-deployment.yaml @@ -73,6 +73,28 @@ spec: topologySpreadConstraints: {{- toYaml $serviceTopologySpreadConstraints | nindent 8 }} {{- end }} + {{- if $.Values.web.busybox.enabled }} + initContainers: + - name: wait-frontend + {{- $globalPodSecurityContext := .Values.global.podSecurityContext | default dict }} + {{- $servicePodSecurityContext := .Values.web.podSecurityContext | default $globalPodSecurityContext }} + {{- if $servicePodSecurityContext }} + securityContext: + {{- toYaml $servicePodSecurityContext | nindent 8 }} + {{- end }} + image: "{{ .Values.web.busybox.image.repository }}:{{ .Values.web.busybox.image.tag }}" + imagePullPolicy: {{ .Values.web.busybox.image.pullPolicy }} + command: ['sh', '-c'] + args: + - | + HOST=$(echo "{{ include "cadence.grpcPeers" . }}" | cut -d: -f1) + PORT=$(echo "{{ include "cadence.grpcPeers" . }}" | cut -d: -f2) + until nc -z $HOST $PORT; do + echo "waiting for frontend at $HOST:$PORT..." + sleep 2 + done + echo "frontend is ready!" + {{- end }} containers: - name: cadence-web image: {{ .Values.web.image.repository }}:{{ .Values.web.image.tag }} diff --git a/charts/cadence/values.yaml b/charts/cadence/values.yaml index d81e3d8..863d220 100644 --- a/charts/cadence/values.yaml +++ b/charts/cadence/values.yaml @@ -451,6 +451,16 @@ web: imagePullSecrets: [] # - name: myregistrykey + # -- Image configuration for BusyBox to check frontend service + busybox: + enabled: true + image: + repository: "busybox" + tag: "latest" + pullPolicy: IfNotPresent + imagePullSecrets: [] + # - name: myregistrykey + # -- Number of web UI replicas to deploy replicas: 1 @@ -773,7 +783,7 @@ config: # Cassandra configuration cassandra: # -- Cassandra hosts. Can reference Kubernetes services - hosts: "cassandra-service.cadence.svc.cluster.local" + hosts: "cadence-cassandra.cadence.svc.cluster.local" # -- Cassandra port port: 9042 # -- Cassandra keyspace for main data @@ -790,7 +800,7 @@ config: protoVersion: 4 # -- AWS region filter for Cassandra (if using AWS Keyspaces) region: "" - # -- Datacenter filter for Cassandra + # -- Datacenter filter for Cassandra (Used for schema setup too) datacenter: "" # -- Maximum number of connections maxConns: 10 @@ -1117,14 +1127,18 @@ dynamicConfig: #################### SCHEMA CONFIGURATION ##################### ############################################################### schema: - # This job have validation mechanism, so it wont replace your database if it's already existing. - # It wont rollback your schema version to avoid data deletion - # It will update your schema version if there is any update avaibable. - # All the schema parameters are taken from .Values.config so configure all regarding before running the schema job. + # Schema validation and deployment configuration + # This job validates and updates the database schema safely: + # - Won't replace existing databases + # - Won't rollback schema versions to prevent data loss + # - Will update schema version if updates are available + # All schema parameters are taken from .Values.config + + # -- Schema job for main database schema management serverJob: + # -- Enable server schema job enabled: true - - # -- Schema Jobs resources + # -- Schema job resource allocation resources: {} # limits: # cpu: "500m" @@ -1132,17 +1146,18 @@ schema: # requests: # cpu: "500m" # memory: "1Gi" - # -- Schema Jobs affinity rules + # -- Schema job affinity rules affinity: {} - # -- Schema Jobs tolerations + # -- Schema job tolerations tolerations: [] - # -- Schema Jobs node selector + # -- Schema job node selector nodeSelector: {} + # -- ElasticSearch schema job configuration elasticSearchJob: + # -- Enable ElasticSearch schema job enabled: true - - # -- Schema Jobs resources + # -- ElasticSearch schema job resource allocation resources: {} # limits: # cpu: "500m" @@ -1150,66 +1165,44 @@ schema: # requests: # cpu: "500m" # memory: "1Gi" - # -- Schema Jobs affinity rules + # -- ElasticSearch schema job affinity rules affinity: {} - # -- Schema Jobs tolerations + # -- ElasticSearch schema job tolerations tolerations: [] - # -- Schema Jobs node selector + # -- ElasticSearch schema job node selector nodeSelector: {} - # Pending to be deprecated for auto-detection. + # Schema version configuration + # TODO: Pending deprecation for auto-detection version: + # -- Default schema version default: "0.42" + # -- Visibility schema version visibility: "0.9" - # -- Images used for validate schema creation on cadence-server + # -- Container images for schema validation checkSchema: + # -- Cassandra schema validation image cassandra: image: repository: "cassandra" - tag: "4.0" + tag: "4" pullPolicy: IfNotPresent + # -- MySQL schema validation image mysql: image: repository: "alpine/mysql" tag: "latest" pullPolicy: IfNotPresent + # -- PostgreSQL schema validation image postgres: image: repository: "alpine/psql" tag: "latest" pullPolicy: IfNotPresent + # -- ElasticSearch schema validation image elasticsearch: image: repository: alpine/curl tag: "latest" - pullPolicy: IfNotPresent - -############################################################### -################### DATABASE CONFIGURATION #################### -# Use only if you want to deploy the database from this chart # -############################################################### - -# Cassandra configuration -cassandra: - # -- External Cassandra endpoint to connect to. Required when cassandra.deployment.enabled is set to false - endpoint: "" - - deployment: - # -- When enabled, a single instance Cassandra will be deployed as part of the Helm chart - # -- When disabled, the Cassandra deployment is expected to be provided externally - enabled: true - - image: - repository: "cassandra" - tag: "4.1.1" - pullPolicy: IfNotPresent - - # -- Resource limits and requests for Cassandra - resources: - limits: - cpu: 1 - memory: 2Gi - requests: - cpu: 500m - memory: 1Gi + pullPolicy: IfNotPresent \ No newline at end of file From 2ca5fe38d4d8a64cae34431c667bd90175639ca9 Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed, 25 Jun 2025 16:30:12 +0200 Subject: [PATCH 20/37] old cassandra removed --- .../templates/cassandra-deployment.yaml | 61 ------------------- 1 file changed, 61 deletions(-) delete mode 100644 charts/cadence/templates/cassandra-deployment.yaml diff --git a/charts/cadence/templates/cassandra-deployment.yaml b/charts/cadence/templates/cassandra-deployment.yaml deleted file mode 100644 index c4d79b4..0000000 --- a/charts/cadence/templates/cassandra-deployment.yaml +++ /dev/null @@ -1,61 +0,0 @@ -{{- if .Values.cassandra.deployment.enabled }} -apiVersion: apps/v1 -kind: Deployment -metadata: - name: cassandra-deployment - labels: - {{- include "cadence.labels" . | nindent 4 }} -spec: - replicas: 1 - selector: - matchLabels: - app: cassandra - template: - metadata: - labels: - app: cassandra - spec: - containers: - - name: cassandra - image: {{ .Values.cassandra.deployment.image.repository }}:{{ .Values.cassandra.deployment.image.tag }} - ports: - - containerPort: 9042 - env: - - name: MAX_HEAP_SIZE - value: "512M" - - name: HEAP_NEWSIZE - value: "256M" - resources: - limits: - cpu: {{ .Values.cassandra.deployment.resources.limits.cpu }} - memory: {{ .Values.cassandra.deployment.resources.limits.memory }} - requests: - cpu: {{ .Values.cassandra.deployment.resources.requests.cpu }} - memory: {{ .Values.cassandra.deployment.resources.requests.memory }} - readinessProbe: - exec: - command: ["sh", "-c", "cqlsh $(hostname -i) -u cassandra -p cassandra -e 'describe keyspaces'"] - initialDelaySeconds: 10 - periodSeconds: 15 - timeoutSeconds: 30 - failureThreshold: 10 - livenessProbe: - exec: - command: ["sh", "-c", "cqlsh $(hostname -i) -u cassandra -p cassandra -e 'describe keyspaces'"] - initialDelaySeconds: 10 - periodSeconds: 15 - timeoutSeconds: 30 - failureThreshold: 10 ---- -apiVersion: v1 -kind: Service -metadata: - name: cassandra-service -spec: - selector: - app: cassandra - ports: - - protocol: TCP - port: 9042 - targetPort: 9042 -{{- end }} From f8abff85481512d6d4aad14fdad0a29317d4bbde Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed, 25 Jun 2025 16:35:04 +0200 Subject: [PATCH 21/37] modern dependencies --- charts/cadence/Chart.lock | 12 + charts/cadence/Chart.yaml | 14 + charts/cadence/charts/cassandra-11.4.2.tgz | Bin 0 -> 49461 bytes charts/cadence/charts/mysql-12.3.5.tgz | Bin 0 -> 67964 bytes charts/cadence/charts/postgresql-16.7.13.tgz | Bin 0 -> 85046 bytes charts/cadence/values.yaml | 268 ++++++++++++++++++- 6 files changed, 292 insertions(+), 2 deletions(-) create mode 100644 charts/cadence/Chart.lock create mode 100644 charts/cadence/charts/cassandra-11.4.2.tgz create mode 100644 charts/cadence/charts/mysql-12.3.5.tgz create mode 100644 charts/cadence/charts/postgresql-16.7.13.tgz diff --git a/charts/cadence/Chart.lock b/charts/cadence/Chart.lock new file mode 100644 index 0000000..66a616e --- /dev/null +++ b/charts/cadence/Chart.lock @@ -0,0 +1,12 @@ +dependencies: +- name: cassandra + repository: oci://registry-1.docker.io/bitnamicharts + version: 11.4.2 +- name: postgresql + repository: oci://registry-1.docker.io/bitnamicharts + version: 16.7.13 +- name: mysql + repository: oci://registry-1.docker.io/bitnamicharts + version: 12.3.5 +digest: sha256:d355eb032d520a4005678225960f11fbce541e4aaea7fce4a7e90ecebcbe1807 +generated: "2025-06-25T16:33:38.605196+02:00" diff --git a/charts/cadence/Chart.yaml b/charts/cadence/Chart.yaml index b6c2f34..288146a 100644 --- a/charts/cadence/Chart.yaml +++ b/charts/cadence/Chart.yaml @@ -26,3 +26,17 @@ sources: maintainers: - name: Cadence Community url: https://github.com/cadence-workflow/cadence-charts + +dependencies: + - name: cassandra + version: 11.x.x + repository: oci://registry-1.docker.io/bitnamicharts + condition: cassandra.enabled + - name: postgresql + version: 16.x.x + repository: oci://registry-1.docker.io/bitnamicharts + condition: postgresql.enabled + - name: mysql + version: 12.x.x + repository: oci://registry-1.docker.io/bitnamicharts + condition: mysql.enabled \ No newline at end of file diff --git a/charts/cadence/charts/cassandra-11.4.2.tgz b/charts/cadence/charts/cassandra-11.4.2.tgz new file mode 100644 index 0000000000000000000000000000000000000000..d90555c784d8a2d091f4839cb84e40af475c77ba GIT binary patch literal 49461 zcmV(_K-9kgaICvn$Jrqa7c#lH%-a zl8qq}ln{{s1Avk>iQfzPetp;WUddaBKH@-vI@z8TpF}2sK2%p%S65Y6RS*0$^`l|p zcmCcHUC*KnU4J|xDAG|{QPfkZf!lx|9yNG&;Nx#4u^gg#?f_Y zVd?o_TkCZn_WyhNnEAiW74LqQ-~Ttez23w8-^a(yzjqdlC*f%nC&4ZEfw!};%R!20jrG90C!|K;=lNF4ZC79>$BvRLr+i_5bh5+}28JPe~#G4%)M z{%MeUjYs16EKEf@n@(fcD?|z;GZv@g_(V+nY;XppS49#47YQ!{sF$5t&wPB7I)U-creYM1gS7F)OE0JJ_ryPk zzu5%7{~sv&&QHR4mWursd(h!DiGK|SS;Gs5f#2bB@CeF%X@Kd&72q{jYVm9@hVT zd~g*^uVA5^W#V}f`$LfbR>gia@Wc+R?L&N#io+lcl8a!7Ea2$i#ecP5g@YhUgZBO~ zh_Y}L21#G+9PI3Vx7S|tx{b!8N8;a8SR%O6Ujl#d$FQgqe-gmDPVudL_#z&h!y*qS zz+iGE4#t6>2CE{9vp{5Leg=Pl`CNtoR(ug82`oN%6cj42iZB|CXT-aP!8C{f#z7ds z^FbVq!qZs-Ob3|VEam#A3<}XdcDy@F5*U^6FZ^)qpNw^h3Sj&E;9USiM?i}yE z=+lJE={<~~X@7{w<5K;Ga8chE%_cm99{0RckH6(H)91%Rw8i8~Rs3sk#joE0VyJ*> z+O8>n{23mXfF}D*i;o`BE(7~ncoL4o>`M4Uf10s2MJW60>;!f?prcfv^bL z`BcOsdJoHQ9A8b4AYj2w{Bv4y6Fo9mTeSOYsDV;pKEgp34BHoL;%Ilb5056W-bFHt zcGB;G53Taomq`q}HZHA^^?e*LWD1#Tm4}J&$K&`CmcAn2{THbx4&pQ=IC%T(3x5WX zuK3d_$ReQ{MHV1L*l&bf1-J{4BCN{PK;BwK{0{;UY=VS(#;Rs|p!-YzDurUhFh#C1 z6f46RxtmZGOyQQEC;6pp9vD`K)k=xRVWc*v!FB&u@y;VaGz{bGoC^65jKv?)_5t{k^bJ!PpP)dP zij{PB(q>BvFxG%Nf?X$tp}?*~{n@Sd#qS>r9qvR>OD<{q`~0p=(^j2)`%Jel;)x$d zHqI155BZIn4X1oxmGZ{%z#m)90xc%~3xY@N;BJD;pB`$w0MHCDuXkJ;peNu4kNsE; z5R*j)9?{M_41JK<(<~f_NeqMoz`1+{tD8cvp^Rkk5;pgp7eu`DDwwUVr17t<59irf)6<7$bV z4ImW&m;%2(h{xgJN{PiYTtW1tw6Ky-^B_|_mQK^+FoToXsamsgi1Y{-Es_>JL!wNy zBq*sTjOnSGiM}Wh61<0z^~HwQ^S0X1ZsX)p*!Aa)t)kcZE& zUoe@_!g{;Nc3k#O{QfAl8K4nY2awf2~sbC$%pX4f#N z4h#=%_0b@hlF?qR+YWV=b{A>z9{VUcNm8T@f9ekcuigMNKRu_>vD((2#>2KAYX?WdgKJNd z@B%t&tNHJ6`+;{Vw}a_*G=OPA__uZ@Lx8e0Jy9%QBZW7-^WX}#15jtxH&}CNdKo9f zRgootHu>6o*9EXGqb7Ocdz5I$sTX_z$U(%B{b=&CkgzJL4Cb1eK;bfpFKB-OeiId|t5FKw z1gbIbEjZqL;t;@yCqPf90Zcqo6o?p;X?9g?F^3mK7m%=es#IW>Op2Xpw2KvDfzl-U zJ_qi5lI8dz5){@23CUtA4ql8TFx5{KPbmiw1dy|Ea0c5lETQ2QDVHFXT*b4*fH;K7 zluHQ*f|krnKN+U3>bNR$C>cO^CpaT%IGF-1rzEGZ>NN?nYBV6popn5{b8=V>2JnbK!@~IsvQKQ7lr@QybmY55&SCUm}2Z(`(@mr)7+AFE$ zoMylecZt!$*o66Z`(5j{ONT5;)MlQhcU+V30JbaG&hd-^TW;MPq|zx+^Rp|rA7852 zE@psT#}hOA+Q!JG=}^JrV-qDzq16pGQ=m1Q#k%#z&DRrF1M)znq&u!;72V}l%#*S~p^|(A_)l?@;#OE8=4kV6v2Gtl5G;_N z07p>|6k9hKw?xJ>(4!F{o&AIAhs51=WfmPZ?ky4VKi<6tPM6`Twinc|7ZaXtkj7Ii zU~PK1lK#WL@Ws0@$w0^!uYEjV4V1xUs%Fq`Y~p|TF7T&E;ok#luCxIt6i#LnaRwzp zGJv8huXq0Im+$s=4t_b>|L;BEv%CIyfE*O9Av556z=8%O=Yur%J_^V0bNxoa<>G#! zkliQs_h#>hCAw@tUwwsh#F-JUUI&wy&OI2(ePG1_%3B~&460h5MZmOEssDs%Vnbei z87cxfj?oe3882xTPr=tjQi)8030rsy;TK8fT5)2nFHA_s#hYpx_n zJK{8shvIYwn-(6!qu|u*cE#)GbTYqt@ICBWi4qAxn+l^9eE$R)!Y}wLT_pgYnE7p$ zD1NBw(GHMi{s*%61X0INfI2RMp=I$|^Saircp4?uVwvAXNLBNNB8UO;G=6l$ET@cvJODR7V`cWEC-A;0n zp|WuvWw7`_kf0%TPuDlLe@rT^M&&)68rNYa=+yQ13mx^0KRGkb6cX zWtvS5a0SEaJS!6DC+FkXAF@3uiN|9!C|Rv_9&x|zlsgVd3~ac-AoD;N`{EE<01SUm zukEFP+!n`q6eJhnAlMlU@E3^?;x%DQz7g^ji;*Z1Ph&a&p4Wu6zse4Ad)^s`ehP%Y zGaTCLBEAA0o8DoKI(SOC&$JEi5oa*Qjz1i>p}ho9(#~QsR0Y|fjm@WR+!^R=TlR!@ z>xKnYZm~e^*M8)m1_+`wuPTbeZ4}<4iA@$T=GI0@#lCo((Bab|ni1?yk}#&zL_Ds3 z<7x)bPWo7v@Rp(?N0I3uB2+u@B|w`EJ|}4N5aiY~)OF6nhQpAJlY{`@yjluARKqU9 z;Ibp9j%P<6RCTOYI})SS=V|a#9t-AR8`INU1dAI~5S1PtBBUcejvS)(6Vwokt|5({BY$8)y*z&qDn~O@uSNGGfWXG8EWN-69?iKN%hhymnXe$r8O;-c54&MiULzU}FB>%Rg9CU@CmMvZh7 zVkm0JxT7eyH&P=&Td0HW`A3fgn9r-0=&`DeI{n*zoZc{rml~?$ z1j_a=Y+@P=YoQmm0}N0)jN|NE!%mh#b!iK=r}j-8Va=r{SJtc3&s5FIkb2SO${3z0 zTW^gG&?Vs@iIEkv?7y%JSqVKzuol}^%R4CJuR)G6R;!QVa25e4? zs%Y7iu!)YnCXwcVeK&Vry=oG3cTmG+q}Fmc195i}NUyRcd9sz!kc1KWe{Ppo=MZ#i z7Njy3WgJezY&r9UDY$m>d;c38Usw*DIAYdle!@F5Hee8b=(7{^oPs`r-cwm`!>y->bL~?FDuC|(u)jG!4#qy2?7su;^3qQNbzBwC`4!`5TzmSfYB#)x(I>2y4Y;ip<--FKvqG*Kv$!b5RYU4TIdMRs>CoBmwx1o!>Trm zc&vOtIf`*P0FJP6JTQV1{_t1y38bhGXg>;|Ug~3%6b@d7cF-xvECJ-5Bt9W#gvbAn zbx-}r_Gz|$EHyock=W>kg_R6t1OmpD4rF))S6lIOHUvBzUKNoa#j9cx4ifk$oj@V@ z6%51KWEC{QBOaqJi+T>G;vB-X?1P%8&)0LMagBPhh&!SzoNFU))n) zZ1&b(hy0M?bjz>j%hQo-=%7dG<3`MOPTh0oF!tU&2Cfs~sWOwjKMd&?#~R#R_BPK| zM{V6nn>*<|vMTFtiRtc11{n8p{u*hhc9I2xv6pPvEi8~YS|NKX+< z_1c^UNl4lh)jObH*JuVe(Rh}rc8>YyH5wZE;TZ6DdOg*XJ81P(V zhNiJ2ez2gihV!6I@F|&zzS!!b3}fRHNFJZL$dM28u(6*{e)zDa91bA~u9S$}x7F2_ zBcr;lE+nM74b>o}x=qz2rn=45B&R}i4fn~72G1T~MnVz_=bn=48K z(Nfa4-&Ivvj_m8SxRB)QG*W}y>oil7)ax`=lgx8XHSFIGi7Q2|l*a3l_~sH64(rBJ zn8|(kmCez`F1r*vY@QP!7!P80dS`tCT^dm}E;f}9|D+O!&T?11xlEvTcWz^){T*4D zSH`uUg^m$c&F0s2qgXrF{5Tv1gR8+f_%4pm(>YBC*IN zVrNXQw&ZiJ-_++m6hDKR5t6p_tF;)<1Yit7wr%T%`Jmr{Aa~jgEkKK;7`#N4>A+d+ z`;l6qt3;}t<^m}LH6i@r*a<)@L$6|dC7XrP*eXSBU(F|S+KU_5%OQc{@fvy zd3JG1iL@+4jkk$0U>F^&eB+F9A?r|NK&h8o!*09GX^mfzThevn-_2LtNe|L}ce zT6*n&DD7Fse9fX-ojPsi79;wty* ztBhRvHcZm&hcG+)jw?2#hn#G~a_TElum$HMLo?4bh$w**U1O*AFu!G+VwH8 zYWF{1Ew0~iEA~0OBJulTyW1s!2+u0F*?JC8!S~)b-{89WZQK{%bT`)W#%VcSCn(}i z!GR83`R;i`EnEnGE8YJucugjfX42YsX2pl>u6IX~vZ=R3P44$ST`f^eN|Z&T@# zuC#5Rn`XJk>CNTj?mL&M^Xiq1oA%-y;5qXg?OTiZd#*!@pgYb5kLG&e)MC;>YhSzY zbBG25j1j2N(<%lDcfenmnV7acj5Yt@0D^M0Mz z&pf~G9raS|9PHb{=LCkY+7glyJl%gyPj5xh#|ip%4a6QY08}v#=1{ll)LjAcX({FL z(Pgm(eC+xh%6(0RiRGG^nzdbMORe@f6#>{J9Hb?c)tjkm099DUB5a1trfw?S7f-uS zyE5<&J>E2bW;R+9v$L#!ynF`Q03e5)-KJF|#FvKkX?U1i|MS&?I%@oGJ^56x8dcmv z3audvCFN_1vJ`B;hY1c$=PQkCo!-@ljQ@_RmhGO2@f`zGZ?4g^%N zp8I1w?gR1?uiT%s(;1>f-tyiq^8PkwlY=b%aT{T3pU5uMSX@9WQEC@EqJx%0bc)jZ z4vkQ#5<;HE)EkD>zsWRXs|_W3_+T8*Fj#@oRV3|Em;{$Fah*phCtX`MgWgLBvl_12 z#TvPsBUR(F+;w|hkN@j-);7w}=G3bqh=F1?8e=LPTyY-&%9Gvd!hKjHk!D)f<7&#M zxt1JOdjWEf>Md++%z&p>UL2UEn@tjJn{CyHOKp_vhJcJt$=!^jd)rJW?Qm)>t>OSQ zqb?n@y>%X_L?+Rdts>h+I3vZxzd{jI1}YDwBtS>RN()Ol1!*cnd+qh(WfvU03{;hpkF*kCfo*&UO_HS?UaY0I0E2w= zVaF3`pIGGrlB>b3mNugq%|Ig3%2!f6rRi1xFWb^2@Lk{!IY3%TPYK52X4de}WD%XT zG&gAVey(Ur+Ip@puVJOd%o8X|FH&8kwawv)ON73ZOuYQsp2`NIYdB-KYRtMWTWX>JR1*@ie<_y&9ri7aaCQBGs~W0!Y|_Jai%yBArDijQ@jaT zBI78Msah3t3WS6NQ(BQRgmR#I;s=bIkh-(2f|F*b#hF2z;NnxnX!S>{^9((lB*4O# z2R5;-#&eZ0B};06o>tse97zyZVUp#jQEX?L*@F?IB%Tn1X6DaLtmL5-!vLd!Sy9CP zTSncm(YPGa_f8WW7DXn>k#V+89xg(akaIlQXz3W4vmce0$c1F(KW!U$ z2fumgu4Hcau0OS6#qv!nA+BdgTdh(8*t_ohXYs@A&zXIr@9ov006E8Rj$Ru)nNv@b zpO}U!(*z?YlJYpQM$S5=(=euWx;wz2@{%48bfELKdL@fxaYRzisOViE(kgS0PB~w& z;*i%M8wRIVA-1j+GS!0?;D?jte4=H(_l|BpYs>RSK87;A=8mJxi@d9dvFD)Yav9+7 zc2_AKm0vZ6RTQ1&MvVo@*!?2ZVtuth;lQPmjbW2IjfS135FnAJPD9F8o#I)Q)w**9 z05&AE=?in0Kn@d(Sgjh!lvS%v&V4>7|Fiw*FS>@5V4Ce_S*0dCW#vtt45ngug&wp6 z&R>ZwfERMC=SNSdIp3&u7 ztej5Q(Eh*k!x4@EaN{RK5o*<>$on=(65HK5I@)>j;&A7e-M6n_?;pQ<`|_6;`-fNt_NST5ABxM2 zZsL&M@0UraGxxDAo%r_%u^K=1R(OOeN*4 z$;sJK!^+?^Pf|4IFE49;hh^;7Rytm*f=XA8-bz0J83k{nK{e~6G-Ni>AF`og0b9aV zMp>qwz6=*EFIXxyJ25mayYj^MQOfC9<&?YAC7bSxwK-7GdCCD^&xLv)-P1)hWJaWI zDpV9tXLUcia$i&}o_Pa*UEX(Lpd}C#$L>)uLdXfUOLD|>tI+6UGSElT=OYaez^tTK zHONPz2yoeWO%L`ASXFZ8t&&_1(D#W2(Gsa;Kj+e8N>IcCLuxhAmzRtYBI8T+hd}9BIfhXBhA zsvzSO?_VgwQupEjcUlx0bC(Fz>#D}Z0&&`=>-84(tM3@#p6Ql>s*W)M{k+y{-9b=i zgz^G{d41j89R+m&wC*h^2hzUBpl*XI3%bijEOC@qwXw^Az>-g0L zW#&e-5{~XKrd6)hVq7cYt66u(K0&A}L8gOU-Q}RIiYDMyn;B)utN!f~zgNZB(=UEe@v2)YT9O)F=>n$y96Hwk54K`6pu3Oo^r zqKl0oWk?38n9bi{mq1SpwN3CvkWhAs`FN(l#krlFs3SuJhC7*B*}zn7seC*m-GlFb zJ)5xN=MBy#@z5=yjR(byCKWkMoGd0?N56cny#MAnJsyBYhlkKJx+x`2a6`+-VlWN=4#{wJvXJCAlYxuDctX^thO6Vx4t} z(!>-cQvDr8ySHsE!&PLUVdLpn%&O6-WPH_eg)>_peZXe`MeExF~bbUsrd7q8<8CD5GirnO1COx_P$Vbv|A z^*Y$V-y?|cSEYc6;zbQeR!FkY7qq?4>5K1W#4oqqvZc3zc31;iEv%Az5`P0ZmhA=> zv|*~QTe5r9wfkbi4VnTAI;OA**zGYsn7{bST%n@Rj5{~a`LJA|wsu_1VHu|#Ll14` zY^Zi$iiCL)4`!4In)Am`LvJ#Cq^{E|6R8!j$URaU0p^8NJ{FFfhobni`j^Ema~CU@ zA`1^nA)jj84zPv6=ib)FfVvmJCa(w`YX*2gE?dov~*ssv2I5aUjOfC7DybEec7zoCj zJ>UZPau=#G^28dLY=FaMQXN4-`VR?}De!|4O5ZeSi{0GoXp`%V^DG91L^=SgxwW5Z zgO8rx%DJU~th!6_{9oe}s#S===U5Y&9w9?@Dl>s}Z2a}vmjs%SLHCip``XZ{UTnr- zu7gE;L^*Uu9Fk~JK1@J3c;GltiUQTQ$?UNZb@tvItInt%CVw?vG9QxVT%CeWeVCcbI>r3^OME zNi~8ORIE(0BJt)j+xDV;RyqV6Ag6hGTHZxj_DvY+|-vT!QuT}{SxF-eJH zVlON7SB;Hv`4@e>VXrFFq(vU~E#ZGiwu{E*aSRksUw{8yc<=a{>JnP~$lHIK0werZ z^Jp|AO65jfbk8f5Jn;@lgR$D3w{BzyCu4tbj@7v#7yw?eE3^Ldk+DKIaC?Ld@98BQ zBZd)E=0K*%BtFHd((!)Utf=orRvr9B{EBxGw_n7`sh5sN8^3xfT|)gTK0UM$5*{uyu);sJeox?cmY8*?s#!-L&bJei=ROh~qLx}X~c^6xYG_ci(VhbK;J z*y|)3fr>V!Ja%{jhu64L`^UaTw@a-?V&_j!*8Q#S$v6J?5Ojqg7!0~wC%}2OdfRK; z-)!~Pg28t8o3&xR1=}HLFO)Y-+?L3&H-7pf)s1P>KqPmk$iZ8Fo(b zO?X8xpIRxZTi@#Dy6vZtKgIncrC1J4^ztn(vI!pB=3itni0{KNJty2_k`b(H`-dFA zI_ex9y(;k!DSe~U&5l#HdST+Ey=VX|R$u$1p96zdFLG5m{Y0*w%F@CC{grjm0q3R8 zji$bFS}>nvceG9i<53%L5riU`g>QiAC`On?1|$%%OJQU+BApH!rd^)C0K;)yJxyF_3q%JBi-b;@1i__MzZc9sE zDP0jC1ViT*CT#y));y3Uh5n)rgjI=E(}tI8#6Vx&j43mxp)cd8)qzwWvlgCyPB*ud zBKgzY&|T=g7Pg>uIaJLkeO#;OhXNoBFBEA zFZr@Lka_H~2dOURmbHAEUyLeAXJL7%Oq~V%nY5l|Sdnk%9<)EWn%t+W$>-xn;Cc}J zQ~qOQ#$B8f4@LDtp&Nd>y>54FV?%Vi-QMJNs&=K9)3@0+dB$)LO0{bsYf?ym=aFx>3=+r94SWE5;~G)@gWZmt2P zx4TcCRGfnB%ISZg z^eUY%Qkq7J?KjVX%AkQLIAu%j3dr=M@PkT;`_CYfV^-mcjbbq4#*=u+&j(#~} zHHz|$U?~0W5oUZ$8y<+&9s0}QH@uU-Cms5$9-N(a@Q?bLUf_-Ps-Y7Y-)zcvgg2ge z>E#sup7`hRH=Drs{{uze`3Z_UV*kY+bOKgF0zAH!=Aa)$f&-_bZSo(%)h zUtG@4}DLgzSSG6Wr7tHpWFe&3${17MOVUvM)702h&a*Do0bR1z)si)H2N!srI zO&nuf;=9*)mAN?fqrcC@jwvm6p+lG*Gtm+`@*Z`N&ZbG22CY?{yeUX1>^K;PwlPl6 zDYxgz{6?7uq|xBK=!kS7bMLc-i6}&wKLwy4v;9av(_9*Ld8s@ImzT=4#KFl#y9H`( zDUnuipjHB4xy0vmK=SF2n8Y0LnVQzl%o#JVD+fJI^^VZhChDX%Ey7$!Ue$92vQ4Rh)l9AtGG{x|V!C5fz z=?!2UL+}oPgBe=Yk&O*?VF`P(z)zTgK@v(U=nGmOIuH_dgM=+%9F~b0o8y7ZSS|Nv zJ6f_~t-b|a9h!()#GVuMs#{iKMRXcQ9~upeu3XTp9RaJG>3O{^ucsq2yX|Caz0t7Z zFBdeboFIJ@bSd+uiWh7}C57to&iK;5N+09#g2}N2ID1S+{p|6o2rx=q`bIsrgFYWK zJXSm;1EgYAfl?;Yd1)S`OU8_fl>m7RowlLJq&k0S0i6Q2W)`|nKyG8N0T(qq7P|B; zWc%zTofy;M0%dv#blPHlN+NhaNbN~tQ#;c3PGtP7FQsdJzwxLvKKD^+bS~4&Bbgwn zZ1x{jhu=ODJah>W@mpM1R`yx5vQ#X?B0T*dQ^5y?B(a_K+}Ej1b@{o*2UW$s4IJFkxrD&clIa`QPh=&*dzy2ZSU^?Sfv zjtw+qENYiLWZ0!rP2|vNN$sF>$=S`#`zW8b74Z7V%4?ZNz#}UGZ z4B3)IvS5^NERXBJJr8#VJCQ-!jq~6NBp;Od`uVoxb1nXHn^V5Q1M0DpHXT6ae7*5y z7*zqm@N=NnTx7Edra^rqY+xx`LZ8Y_uG9(LUxO=+N`1_OfMFiw24!Wr3{sbGUpa{qrB^iazGdCg{$f zAku*~7>)F}yo0aQLjMJu&>V)3$KuyCju51l!?^{3220N$S0MFaGYZzJ(0z6j_d?B4pSSnIBJS6PL=SW`z> zu+Ne&6kpJY*IlJ&!O*Dqmctejue6aq0t(JBzG+0)&B@asvW0+VH}QXkjb56e=%&{* zjI>H|z*2zGCUBMGm<%=M;xc0?kZX+7@scM^oB-f{CgUQs3)d*{F{~`bx#oO6Jk)jN z!gE5wO-kg|{!>&+jsP@DbfiS3%9mbvssx~H@9is3a^)ZF{LGgDxx2ARN2LjSqH5%W#LcY}yPAwbhX#8%Q zYsEI%xiYsr7wet_*2w>@^GKHF1Ch2Q^99gghsQ28Asngg@sFTBVHY9X?w`%zc? zFim0mtKTKd=yDxNLQMyt>m^~Vk{8OEl13N1_Am}oa(kC7ORYzM1SisfVMf8RQ0`*0 zhsKKr!A|GF?IJ_&B}p*iC|~U4#~3g9L8BuT8``m*^B2#>+AkODzf8hYnKJ8<{@Ml( z&>o<>U`s9&`3mE5aPrg`0EUZoNiE-V2FY<4os;$S&#G_i)4Q0UnOoYfJ8blBlhM0v z8NJ)m=&kPo>|`@c3*nE`&dcxjUr-*e@kP+a@vPC`^){$uS=+$Qq|t^|7JTr|vdQ=_ zHZXM%QCi^y!QwJO8<9Z0w=dS(SuDS82Z^3nT;L=6Gr@51t?dIXzm<|{iCLMzH_vHwA%P#|M1m9f`MxV)yNv7yHNiZ{NVLZ^h1=zlp!@zj?7Lf{^Wre9IXEh!Kq; z8lsMZ!0BAyCCbse1A) z9}E0{dcE#?{``Nl`*8k$FCX8{ScM09r*v+zAB_?p*wbu)dIehZB%|z022YMYu22m> zr|`z`eM(3lbmOO5Z0lw6rk9ys)TvgWV{B*o`H$Ku@12t;lPf}v5&Wh z@nu9|EggU&PSv~i#81w>DX5y~!4<`Lpue>AJZQ@UYWSi|j_`Ffi};Yct%lIR7$14B zk8!uI<9H>+EE(4bC2BYx5jyUmeXSHkvK2=WY2=-|KQ#$Id~Wcm;{R6M8JqDj=ORy_ z0A8RB`t!<}pZ+zt5?-#`mh(n-W*sCb>u3L&V4z{w^xRaG@?@y|uuLA5$(CE9skB5| z!IjnITdQ8NUv*Auig|~er^+KMZAlgOaT>Nyu1YG&{E&L=q`v7=EMx9ynZcmw7eo%y zj7C;T&vGBaGecDoV;cFp@f=fF0RE-UO@idq9K7*Eby(urQ>nYRPeu#^Rj25{gT0o| z&VlY!#P#dEQ-?p)co-osWWw@<;~=|HPpO0(^i+4IW|km<)1Pt@&ca6 zNL#AQn=<+3nKeS6#R&VYa;jyyPSslf)RND`2R}9Be<$z6b^2H!|F3VacXRUp+Q#PA zgZzIVpU+1AKMb;2g7*9Ttt=g~J4=T?@&RCDIr+w^)G%J-_zeD)zxdW#v@1dJa6rB$ zxX-FPu$fiDDDX5dlSb6eAO}_|myvCv8#5 zGw29nv->BpA`eD_DeT2T;*Zn>u9R( zjT|(c!7yQwOyD0*GJ(I>p1uy@-vTV~Y_kimRF1wT4=>-`X0Ew!)+(D@dnsGPKTS&Z zQxoX5TrX>zTQ>n3c$CxJ`qPb?KvQa>nm}*nFilxNt`GEf4hP#?)f1P)G$m5HCe)mT z1Q9%&oxL#h{=IeN&{je3G-`T3VxvTo-^Yy2@y!YCVw=HB7q+rz$)Nzc%2 z{qdhL7APX;OWIzobY{f+|Abm5b+Phm4BJw36~>GCqcj99(n|brJXe#hyZ_ga|6u3D z^W^IrfEU?+x7N1u@?UT5;r#DjKA(;Jr!zZZIu4{_Xg5*jj+A^r{EU(Ub24p!%rY78 z9PDeaLa)*IKBXHRO9VMZ%(55FOr+UFx@OC_@|S#viL1>!`77rz-JqCE8Jldi3BQgg zqQ=uPG?)k7#A~pket9h7hK_z3)56zoC{nL=dPrr4 zl~IM{ZdfHE7bZs=gv~iK`P}Dcb>L5Ziits|B-M(kV;fQ|s}tT>6#V8i-h?t3wS)IM zUGz?Z%txR6LaB(e;^TatKV`G0E{q*mGtWj5n-t7@lPuky?EH=*-#HOD`341L}OjW`S+?jBGqDte;|oD-NO1` zXeK_uT~^+urOcJc2hfN77YjY3@W+Nfow%uy%v#dv6@9_D`_wR?zD?M z4EFfupL<^KX}eou{BsByt!##ZTP;_<0Ayn@YIBYVqL8!Llfw!2mg=z`Fu9=zvPkX9v4jg=cFnqV?&BE2Ux&x9xIbVIp?to zh$f~l^v6noui`zquC!okN9dDi>T!E5&w2BQj(M{?uPS32kx!1w%Qc-{(1oQ<{6+~N zAiy@;O}9u(mQh?uSuKh=Abx88B)hVt!n&2wqEQ9fB7cBSy*mE!KQ;J&kR)+(hyH)- zYukDL-`n1L$p3gRpU;5*69~s=3El2Q#Fmft;{ZAiG&fgYs3>%_5EWvdZ6lBg-UktF z4?Eyg0X0JGvExF3eMzjpg6hWU1vG@cyMpjK2?<9(rAoXIBQqddZ}k zH;V1*Z>GNkpR8@lNvBfAScFouZ{|@&q6eQBfW%Y8f-MW+DRHrW!G&l){a%KJwk^%& zD}qtt6#8ub6sgGnBO#AFC?XHnqfF!50^nP0glPeEi4S+SY^r*S&l`7y0i8CNdfE&jMc(kH@q#_@~|+W2B}}S^n5R ztuQ#MEU5Do{nFN?DcATa>wDjtDpU@-G-)SWd}HZ~huJ>SS3t74KQOmS@Znp`!qIFe zKm9cP`l~- z(RzKxe<0@6?=$jz&Zj`~syh>R-R7efBk8V|5zfTRPA%uhqy+^?YECF>8AgZK`sD zsegq1RLTRG$d4MNgUS9gwb`#I`_P}#&Y#jgdAaD6!Ztx8DYsmKl~$5oaJepW!*I;( zc?><1As2-g*Md&=;b8fhG40PcaG|-*sfl#^fB(P#UmWf2et)=s{5PTbz*{*$bE97> z&hR=mO1y*VII=Lq6cX~vHHxer=oCo9fDe&Y$qN?$MFys_zmY4g$|EOm$49ACrO)D}9M4wZB9<(9joFmkvx~5qQ=t@H`hO^YlG1zh%t_0uvo#QS(qpwm(7;)1mdD>v1Dg? zqYx99u9Ol5O@p2sPc00w$hq~aW)8_U7_-ZoOO773!Nm*;xZCePv1@{ zeDA~?SQp2-aH-h`(A1pS2$Y6PE@5*LXoK1S6**i6cf_P zB-+n-*~AG6f3QXE#(mXP)?;BuG=?K*noH z+N{3++Y{fqH)Pw!=2UCvX;2}(IM<(()m&Y%P0J@Ol~DC$RLiKY!tQz~kvkYjA{bLf zUq<2SOy(kFucx*~gr$df9?tClAwG5dzhv_GJM#bPZEtM$it!&0_dnmu=X0_D>hKSl zE*yP)=LT{tb%}y)i_70{Af*blCLPU2qwoWUlU?br^8!3_O|^4(t910)v$}(cV~<14@7 zX{{8a74lm$etK{Y>g>=2;Lyz@e0v6MD>YKmRVJ2uq(;yE5>5-#VV~g}Jn~(lGDQq# znx;z%$S#&*!V`a-$7qV?LZgx0eKdt9O*8yF&7SpM7kIFor!*{B{U-G0~c9nV9>y}IUD~-_+!xjhgayD9Zf%*g$z6k@DQ(2V zcTPUIg8y8v#HS7|>dkQwon~iE(d*J2#yE#;f@%`A%5I$RbAw0a(l1afe6>mTG0)^< zW9KtQOO0bX1CA3;BwM)_axIrE2CVEN3Lc<8-JiL!zqqaCnUeDIQ7Uf$rg5G87_EQ-UEZ3)ure{!jPDGn5NQPS?%=|`<^irZqW~i8;CX7^n+UhU`0+`r*(d zL1xiL+RX4=klrlzPlT^|2Su!c-dAsGQyp~87xDN>Jd;-#sx@>>ZU;m1@soFJ@~sClTeNgPP4(q~=IS(nwud_}b4QK9 z0t;a7zXCw|CHw*VBB)Fjf0B{Z!d9gFIF&WcQd3uiXju7vMn5a*MjzSx#7Xqk4cr$D zNqbm|$53EyH`!TWaSDBJP>zHQzf+UIs0@af^kWS@ah21s8tO&p=Q)r;9_~hl7p4km zg&vm?&V`?X>WTYle(2l$)t1T-rDwjZfV7nxcOV&{Myf1WI_Pyz91`CF9m%kw9J=I) zcW8M^HdVx^oIc4@rDuS;@CnetFCIILnE^5#a?n{FG;2=;K$}u-qc7^02jga`@(~mTADc(Xh;Mf0l*g)H|iCBJmbU+%mO* zg0uyY4P|Zqc=GR$?BzBU@g!4xrR)l3ktSUqv4F8KhI1t_&Rp1JkrRdD|i3j|#*?fKivG}JZUE)N|N~WmgD}Mh|)Ko}Nur2U@wp0sj)(qFi zQw5n+psAv?<-X`5DyNRP-VB~R;S{baqF3iLdr6k49ECICdD9^qSE2L=7H7!E!;}-d zQW4Y~s8AT1i>3m)X~AEzKJsc;9vpIPzgARFab&r!re?+pVIdrDQx1&V%&mp^RKrus zR`M!mZu&hbeitYSs8SDb1v6CO^JJY}RkysNEc4 z%lI%Q9Jk~2Y&IH=-IZpo;fnDhh5&n2Fj@1p@QfDMuxH8k`OTL2PvJ7>^0qunUjO*9 zXoHyAtE6B?EHA1OmDfq;0UB6p31xhvV3Zd|SJ~U~GUOBAlx|Ps8YKNGp+c&wH7p#w z&n|1%ko~-qDX%4ao?J0^|3YmL_Mj?S`P8>*Lg;l{L)(^6a(_w>Ei+hq z`bdI%P>vFQYU*L)&^YsyWxLN~tmM;mkZJADO8GBQcDdSxVhlG&>dk> zBWOOuociRi<1{4iv;a~rd&c-93|tdjEB+=EMH)ropLtBO77fgQ5I7to#hpTDHwGOQA{&;ER!D7p;vR z2g>i$U}whW-sR>~hXgDt4r}mWDKOof9hmEvqKd!DI|fiw`3a(&T`l!eu1D3l6DX56 z4Y?6TrrgY6=sFZsitREw%K14U{Hkj!<7SDWtw3!B<6k*lR+|5EEV!~`gE`|XUwl^q zXPLu=8UNxjer z<)6r-uZ?MRcT*du5{0zTBiGtospp=hkm_x}dYZY8J!kV#zk3!8&W%#f0dzxk+w|j9 zVKlXG%?tl7)4#4rnL+&J8K^X^+A~oqcHDseOWSK(JG50mF?DzQDk#`g#Sy0_6y{?E(ee zIX`Vy6H1iJ``6brG?cV7-`b$d+bYE3R!NAGPqkFPu#dw)r%l;gW(?R{V(OOn_h~i8 zVzE-pRw`#D#j7>6lC{xX1Z*uOtpo~J9hMlfCUPk%v}duI%Zng0c9(ULpN~1nJ}i<+ zO}v*7XRR5qhE%FF-`0$jjqJ-;qldu&v!yD{Lxz|If^)&EbLx3}UXj><$$Kiny)9AW z-*!A|t9SGEwaUPmZ|Oc5j{H&3vZH7iNvPEpmSf_To67tVxLRIL!Io&sN6D0yvid3b zKv9&;!dlWx&p3@#$IRS{DaDQ2S&j(uJLqzH<>L0NjgApjX=z@9K=Ykct+hqg9doBb zZcZ&&ayy}%)|NY*ImVyLK9+6xmJMSOHw>zUx@G;~8doIP2eqcP6lUO6olM8YMf zphB#?Lo?m&st2`4bSK=y6#YKM(;U}Xriz1*tXy20bLdvsYiwE&8C_?~vP#90)oIBJ zyHI(Lcfd((VfA{f#1(hHU@9&h&+TzH8_`R0y5iZz+@@}s-`TxQug~&Ib=xnPTq?)3 zfMhC2Vwzj#Ha*NMS83c{(*CQ9FXIEY!2fHrx4EA8|Jr`Y|8qZ|&(8k4t0?T&&A*0r zKO5_>8EmETS3`X>+pp>AZcM+W$X&|vYg)LW;kVdOsoi&Yf}&xyk6ED^WaV6`%DQV= z*{sBf#uDcC>B^p;g^N$E2)e>>t8Ift;=Lu~Y^h21o~?YJ&cw5Dsg$ej*AS0xm2E5p zyM`E@7jjLTytXP#F7ssQe3P{sx@BvP8Om~Ve?41GbvrjyW=z9hh^2<-dnugTTfrSF zCwII0>_IG;^ZY6+n@;!5|4wh3FYmLY{x`v^G=?Y3MF1_(|F*Zc^8P;?>ks}v_w)Je z^uO0gVK1KFy7s3i_p{Oc)IcjWKZ-)WnbxQJxEr0X1f@%9e5$@|t}HyJw$hhtt6b&t zlfhYtm;KC=(gTJ(Kvwa5O8{%N>X>T>H(K_OEma%wUa$()F|l4XonA}YQ70X_HfH(& zWI_GK>g)Xa75*m96#*rN(0r?(sY;xmrRvxcyI{3yENP^TQ+&(Xu>*lS8UoTLl$%8I z=yodHv8|IcPT@nhD7!Gs^JKauBZR{A9e5!yK;PC1%!$*)Q|ZpZoZHe)gZMy9oVqGrE0x+Yb%$PAxyw z!yVau^2l7y>T_keN8QZkb5-VPyqv|y0p{BFo~xY4*Mqg^!P?_0+O(n{Y&~~w>v1^e zgQX`=fDd+_Ph;m3m~NAsjczij)?)uMQ+a=Bzl+fHd4H?{0seE|Vf zwe339qT5~S|97*zZfw!vb@heYbIQf&FK#yLTXHlvys;hUx}Rn3KXG&#->&~pue-V3 z&D(!Ax?2zSpZoZHe)b=vuoutoCIPS_-_OSoWRJGm9>ieZ&?01abw`GwB6^oI4axeq zVjObYsxS}DH|w}fR2X&^nwWBfsx~rxZYCnUm;oZedH9 z0?QAL$Np*{kH94Y?-h@_$&=&lGXh$FqLrydy(CwSKZEH z107x6;tn|n%iY`Qay!~*WvNRk&H5rkoncFhOmwnj4Wj4ru)AmNb9JrwEn4OzAP+XV zy28foS>;IhI=K~9vj*JPu^`B1ziiu_9K;gyeqH-qMJqS8(8*>$a~oZ4i%yjUeU~zc#kF z^7nsju6G~&zwYJp+1dZ35e&E7xH;8k+>3g7iyhMTe>vGpo-6GP=s5pn@ z7{;FE^i+j4s&pLhMHlaUE9q7x7{;a?=ra&XD8G zNep{_&sE$3?cbDuAJ*fz=-l#p5;_`UGCL^z17vF1Y|g0l4$<7A8X) zX7;UT3V{Vo)K)$g%m2SHI+l$2us@6PKX$jba`Jy~tM`!q<6b_Wi~N6ps|VOXnkBOV z-=!uE9lqKkc&ncy7gVg<`8TMmjbA>cI&Gc5uAVWlqGL;aSa7gZ%IJa*(-@XJ3{*Gp z@#C|vR?cT9!5|wGa@3aJ))$|P62w&Ajjm0yA3tI}#jW92I~KpDafH&zv*zz~=Idbz z=Hnkfer!TrJS*DT5ba^BM96zS3!!8XUBJX8g1|owFM17^JM`FU{&!O6BvH0W5eWn~y+b5z@lNGq`Z(=1MzSN9JEkEwMtw zdWq4j$@4gC22mh7mq%W%oYu4|(?*xQJ^2-1e3i56>0)v=bx)3gk%TGJPMKgJxBeu! z2!l)7>5+9NQUw6vm1G7wGr?OXrPPng72h>7^22e-OgjSTGaw7T!o&i`r4Z}IGh6=4 z3k%Nqg1X_NsZKYkMNvXKhe0+=fP0U^1SUY?5J!L)!vIzB#118O#fdB_g?Cy)C_KW> z6U9+mmaao`FegYCIPo^crOGa16JF1y!?kA77P62Mo&Gc}oih!};bZ^i9ZY?Yq$s+P zTZQ~7OSx2L7riAdqfB2HmVSZPW|pV}IG|^I1$wna=@1OktXym4bK-(Sl@aAs7m8m0=wG^ny$jr*Zy=>xx(Pd$1Ejfc^VHv?SQq&S{o>o z9}rL27!ubc$b8(%#7Y_j;!*GE`u0|9u|zSD03SWioPsUD>knQ zCT5nTz+z3$96P8Q-g6)C7Ki2KAxJHIe@FKHH-Ji+7vl+VeQLPrPs4ZWZrJ8IunpW- zC5mu+&tj_hhe*%-waqP@vd{Ds+ z`!Gd&5pwCsvgHv`R$J(dVq=)55>>ois!NtZbwG)j;4&5F%11SZ0)uKf1pf%eIj0bPv!lVH4P0Mf8r+(_GG1F{Fg-zD1y zxY1<{wZ7)pO%n94Jt$uzZZsQ@uRt&K$2hM;R77a7UgL*=TbDaF^nsA40`yTIci=Pz z5dRA5X%Yp%Iv7Qy{jgX2IE2k77_y-&OvQ>s)r&VrSQfYGbQ%m`OWq&B$XH&cMx<1b z+PH^(>@oxbJqffUflt6oXy@Y`3%Z1ZZUT}{)Jsh(s!U~pT6kCf1GKj*i$HC9wcym~ z;8wgt#uZoAusTnd1~5;BPHWunGQ>9jL}(!q4g=JNgJ^K|Mbcumh{@DwhG!9`@<_vO zTVxpE|_dfCcjZ+EzC&|F3OqKE!|C%ja{E{||AiNsQH*&h9m@ z)^?(4kevC`lx>OWAf5)u)9KWof26Sb0MaO%x91v)PN-X0qjnI7*+G?w5IW#T_7=*v zv%71)NkhWOG~iov_v>PZMSC}5em{jq$$LVbEF&Y4o&@s2ig`iipOz@uJDb1%4ui_# z|42az@uWfcqvLN&)8QP~1C(NAfR;Y|MKkynZUw-5FM=Z~-^Oz#33F4iGk69JauOt` zff9+FT20wotITPF_F>DMi9pB>w2{oQY})&`>lJ}@EG}ER_q--3eKuo*4lBJGE2!BY z0rCaUncR>jo@@iico3lUMMMpCQ)m+17zlA^SvKu=IujqBB_OO0Q|}a>&rZBB?xe5* zopumYsdtu5#*YYhZCrNrt=7t*k~zxNru0ez?p429YZVvIVuF@nXnG!|nGU&f4`!r` zj$5>7DKA-^?%FIhlk2spNX;^={vnt)sl%dN#?Q4i^)s`e*4Y2U2~f*T41kOBKXljg z{@=ar?&ib(e;=REXaC1zL)yuwNsQ;(M0Qer1{IutX8tMJ+=d~LWTulr$Wy{q!Q7Cf zd5j$ypZJ4w@+4se7iRM(KJBK%uw=E{gSpX<5Z zTc?Jnz;lzUH+juh1LmnBVr4~k+>&qQi1yr1Pw6|#&?URR}m^UxCx3mM4rmcvA6fNRfrkt~W3yc2NS8+`n zcr`qW*ROo5ln2}zEnPFWy1#PW&F*ip7i^9TRij*169svzbqg$Gd1z`tGH=0sYR+>P z=)W2Ny6q|W$L4}8_p8Hyz};z_03q>Xbn?XK9T0HT@kS5rqj*Qq_#YOjUbcur1tUN5wcz*Z09LlV;QkM_SN|BGon z9ENE!qp0SS+3+-&2Xin zjVDty$u|Go^VWLpZqqI2I!K5ngWb94odlWRo98=Ym7VVfvkbn0v@YZ1Tq3U?re&Rj z&k*6;3-JxoIjmVqC4Y%70d9?rq#UX#>*I+eNi@maB_vQ{e&GOClS@hb$P)#`P)NuJNUz zTcYn%AR1VI5~Oo{JsrHL(szhpEq%$pT2^PeT&_%65yIXlTOZf!_?v|tmu=X$vd`au zgbttY?CS8%x|Ech_=C@Eoh}xy80cAc7AN7~>ELv!S(sP-_Ez-n;fuPHFi3`<*&Hlh z0RXRAeM_OgqUP;ke&=BS2sG$$o^QE>I;gKId|uPgtuqbMbz%8+P+9l(rs6wXV(5)I z76L^Bf?-VySUUA| zm0RbLcneF(l$FldX8DXs@AIT0*~)wj?F=VjifwW7iy3pqYoN(5ZRk^Aj+Q|!g1xTiA?5->SGg3kPQ=8p)3+9#*E( zZT^lSSc20q&Jxg7jxjo(!3tBmZ;ElZd@ZDRE`~j!6U(R?223!h;`FIy^d7PA6|u{P zk@zzV3~dOrF^3}iq6u&W$p}M-5hJ5`hS`gPB#BYvdvZ+i%}FqcF96WXvw#=)zz2~( zK7E{uNf?EbSyVO~PoC5?De|V7TCh6d@fK#9zpq@7$4TqSkN@-W=O-UOe$^B!4ti`q z8Y_Hl;f8n9^WQq>|F|5pkRZLfjsw7g_}}$n{O|huL;lzM`Ft+t|3YuX7Q-9{c$o^O z?FLc={xHEM`gRH(pM|5$z7L|XM>;ecrn-{aBrBJAbwyk8ayZ-0!M?=@ z606$zjia_8i*A>|?-W>LN|>$$2~sdQO$YLgSo$YQWO?HGmG*lGGsN8lI6iWH=HB$o zgP9gFUC*N`reYkP2L{pNQt?vV9Nr9@%nk?d(PU?6Pf8bq-vr%9O zGEQszq9=}ack9n1d!Zh=2(T0U>PPAbK<;vdO0FAteS)ZfK#Okv(4WG{b}i3u1D2Xt zKFL^F$zLC&JW8A{TZV{iO-SihzZEVa;#P8S8$u)q3)p~ZAJ8SCB%$3_t7sc&mL#{B zw>rx_xq&?#GW|hIW+l`hLJ$@V8RsaHW?VaH=4S4hOq7^=+#IDZvBRclCbMWK#S1jm zclo0I(ecu!a5qB(@by&~%|7tm1*tno40EJ)3NjfB$nnXqzyVjS6$k~$uU0N4%-I4< zUs;=!R0XBzORDd7Ao(UmBwcIVF4*oU%RRc??`8^R4knr^l=9K%DLD0$p_0@9nUPA* zqSr5F_e{bI*h5a~qCX%f+vhh2uxFJupw%6$E$21M$DSW)c|-bIKpp;l+w$8@KQ-h( ze*_Hi`re-l{Xf?>x3}~8Up627Kkw)BxyXM?{PRT`kMPorVX(t=cf~t0DX5K-=m?8E zu@)$1@hHos9RK$1+h#t8Mf3U2VH5xUce6EL1gPi%t4xz%1X^b>d@)1S?FdFSoS`jz z|1^sAz7>0tu zx_y`|>oE_rZ#{9sn&ejLw1oXoT#UI7pio`M$q3kukS6+~S8Y%81+bIhjph*>xZ8Q; z99^TICwv0`Vw^`z)Tuf{-SuQ|g5& zYhl7JPN>5qTK0QgtAK=(nuRiM?ZW8oCXf@TCJF_wzty@eDMm_JnHBdo+;w6=@6%!} zXs%Q-^(Ho%sBlb|!dx^LFLo0xOd=KGWgRd%y{^lqE#*-imFSsl<%{Z|L4P{++{LIX z^#vsqG2~F_&f4Ge`wZUZuxjO!D-uH&x4VIH6q0c2wxtT3P+FtBjW6cy@_a zQFd`gSWb5zKYpfE^wmEC85Q}(mq$jpE%JA3N9--P-;<<#dmEu6H~+(Lghqz{-?9-d zudB4nS=Fc8?w;Dqw>FcyIg2V`P}kh$YBQ%?mv_vV>h5Ttj2(B3_)Fc3O?bapDhyiJzRqCQ80@ z$X+h_%9)f7Yw7NJLL*%kB~j}^X*$xK!loQv;Pp1DA%U0y{tM+p_VI{EBTgBP?Z%gp zxpvBGfR4%|;7F_Fc!}r2eEbdQjRWaRIGK*G1R@U^*f`{qVwhauP;rt59MRl(^hg}P z`qvkSjmG=;?=cy9JPsOX=sYJc&=8ZWwn}sr4?@6?%3a#-c|%GVh@0{Wk5!#DYRZ9D z8;w_SOrC%+amH6@9YeMa@AN_p>HKyZmz(d$&Uq7BMOh6^O zrVLa8dJlr=kOr+)edl_R3_^b_0``Seo&t*SrSBl)Vqbbe(x#|9Br(0#32+IFT$&Qy z|5#2uxpY_(u#P_KPCHo=1RZqNF)vjUEi2RFjS^q`y2_~~dRAy6pkRt)P3Y!sH_N5Tx*qDiSqbm^ORSf}y_fq($A^FW z<;~9PJ@|F-_Gth3t@@o?Dt{4gK&VeNZN0QJgwx;35 zHSWUotc+)r1N+S3B=0?3g@H^V4CPZvZ?HlFI?Lj*6|ublPAaF^vRRH zpbM>$awOTLaxuRrvcj(369Kz1LWn!NP4uaRjV{HakMK~RcV%2)0RoHV>1wpJu<(7l zP$~^F+QgDMRcXN^sD`B>5O|%FSv^()48e1UjNB^rUPq0;mzTnFau|+iwPt+rO_q9MSH%#dfqIDK{3AKO>CFbU76x?wuEbk4C1`Yaq=KW6iSo~; z<43Q0n=muq#g}-K^{TshTXP_ZSk+a?0mOo&IDls)Cb75b#J^&9f4bOkTj4bWE{C@vMn|4Fs57DrlaL;Y*%mS@V7s}I3G9cdWlY=5K=@`W4 zE5IR%PSMct^w5Et<29_Q52A|@DyxN$D>X`Im^zi_(UKFikP0-2_851GsC%Dq&&5Qf z(9mo;#ANdNrg^~Br51<}XQ{)zMzWZS6_L^*t_V|ZRiM&> zROm+vYFOZu>?-8&)@oD@x zmea>gdm~z+B{mePl9U~9-TT?^%m5$(5+JFE72C%=yNyNSF&GR6GlRhl?7`g@YjY(G z1}Gzf!`ke^RCd={x8Lg`nz|84pRk#882K2RVtiKkc(DrLy6AC_V!{DzydjVWvT{I? z@VE8j5T+OTIHtnFdss4wV9djsD{1M)C8an)=0aOY1Q_H$dw2~U=O6in*j_vsNf@DC z7IHs*SPY3^5uedUpE0bC$0ha#vz8=}>eslHTil za1=_K%R_wTClReE5vK>%SVX4#Ye{6R03v-c^sXxg*oVj=-Ouny^x8|TWS-8ZVs7w3 z2iWKGzpnP+{@6==xunFj4qV`auw5GnhM8xP*`~0%ha9P~=_fp2rRG#~qW(ce<5`AA z97(EB_peExp}kEKd+O=0@fE5@_jX7C?eR@aj5?h|TDDQLPJiIZ1a{tY@Er+b?uc=@ z2)T_sQ{MTnw4(wR``h-<`bC=$v_o+y+U0W7w!kuqrj8Q@~L3RM@VxenpLaBxbo zVWbrNmjN~K(ZUNHD*p|CrEQGqBBo?1Y4!1|#-G17(2)3RV+a1?_<_IKuZ<^^tgy8q zaCXDcRQci=Tl=-~*Ix}jv!DrZAAdd-7Xaua608};l{ZL(h@CD&uV+LT?py#M9OoHX^4o_;%Y82J@VI+$|3>zo3w|W_P4lFY_=J%H!klO`U7W+;gEePjgNs`4?RSCGVedvri z#fw9sSWK>zC%Ub8$mN?>?+?G5!hvLfnB!7~r!{BX@e%61*J&wbyB8wldtwD*xSH}o zt51_FN$5$jMP_xnN@T0N8m}46{sLdPk@be2d&zCl(oR5dOCdqCO{l_3%C*RZ7NWFW zhT1Y1LFaTMSKgu${0mPDNEk{l7G)N^5`6KUxX*Q%3?Kv&cK?+0np#bDi5ds~%2F{k z?H!-(i?f5QPf}S##%@qY9c=QOUS7*i8@Pcy@Lrmw4- ztw2e~C7)y_j&rYbE5^rd5RI5v&!6@u;OD+lk$YFU_6gPHr zcG6hJHBSe#&VQ-x^TR1pMbjZWsjes_;t#sjsgA|UuvJjqudb#9 z6~iw!fg=EJDGVQJw=kG=BnkU5^nozhuJ3pwF6vE${`$#v$EhSBPi+;xknJL$0@P5U zioC)5W_N#`wSVexO2N<_Na(GVj>}1}+7OQly)?B`m6IQLak9vM8WW+7v*|!9!37aG z{K9P!tq&MlB<2@XrPxZl$2c4OB@I4a=LaZ~hki(+aDrTT7>2YX4kpOKX=N1Z=J=s{ zW|=MSjSJ_Gja;kw-%VvaP0@I2V{G?-M`3h~NqG1rC7jj*G+ZX219D;kh&YknXc90A zMiVsAF9P>_aZn;HDw;9H;r-FU^%aup&>KraD)t(OnI@bWYmwJnMWB6g0s@#oh~q~G z5kEReu0kHd3F(@IJRy&eY(- zR4N>LDU86}KpX&pED>2kThe~XmD04MJWhGbCmHQ!xH4PpkXNQ_niKFFo?LgVyuP8# zgKX;OhV8jr9>-~Ud~WBtVdVV{f51dFXL;C+^&X#To)pV8QC$)BLrW#yt)6SKJVl;R znGsL;4Lm*zd0=)LjV@!wiBggyb}O3DCn}vHVUh|cm8-*L|0r@E=WdnC+BxEz_4O9E~-P&s2|bMaA`IC~abI|KxOkw|%z1$5Wcy z=R1v;`1nlu7$<6)$W&cuqRqWWhQ)wtLTQYpD6DuSB&p+%J5pn^ktUc0no>iYZDWYD z9T~kT4H~u1vY1h%N?a&^`@#s3hP@L;7BAu^D0CMSoZw&&H0Kl0rM>KAeTO_H*eWPP znV&=&OHYPZN*&#lka}zDj3R-tRZz>QXNrr9s1oKW|H`?Jp_ovc`)pN&+maQs!g#-@ zi;w`ix!AtTosMdX?YAeHr_yNlrbH~gJ;}4!GAC)=c#nmtx2wS|B80;10uu26112~% z3X7X4v~9Q0M=GeT5v5B?!$?;)k_Scwggav`0*Deol=#2;M|2eFm86VhtK1yRJy_L% ztKx4z8)ryNNVPx(gix=wUoOi%7;ep&@wi-{)o6?~&;?ZmIyc19t zxqTBP(CMFl*#Kz=&;Yl!c4cwpT@^G_JvKl%AK44h^4L|28@y`!REkYU69zy;do4fg zIewP|LbvnwXoxzLtWClzuyZPsjvlRl+)KFN$z2-|F52lB>3hx~2;;=-9fo~3GjH5T z3rSXh2eMnm$r(rCIbe#;dCuL8!OF8@;n|!P5G$_laz=aM__4w)On`_M5RhHb7}_Z1 z$}KqT=6AoE7-_`5>y8@&F(yD@v2t?7sTsrzhe8741>6Kru*f|qwRcZrJ^Er+qT*Ji zeXX*ld@-v@dB>b8xL0OmwkzDI7k`#%LAWVgmdIl~1zcW!=<4;0fXjln54h#lNLK~C z?+rje?H23j0p|r%Zyn&F@zB|R{$j^*TJW$FaqT5cq%XW&QtPAUCw6C<+oTGl#!r;KgQYywV!2_LNN4E>J z=(wva#==(xa8kfdgg;d`t1kpy?$kslB>a{uIk4Z}rc@u}<2=lwz8nh~s=*et|DN``%ugJ;^0hvmS#~dJ{Culdd0F;VtO9MV=1Di$}m@l z9g-iIrOXXG<`9xp+xx?p{$?#45vXJ${dVflhVHLpOr=5oXz0-z0w3aE>9DotQ&bQv zfGZAcdVSU1X^F7c#<)%)O8t)Fe49aygpi=uX@LICm;C5@ZGl6n0l3BW&W3T}31{&r z6Q0NXAY1I|G2I^L4rw+|w7maW9l)iEhq|Hx<4RXTx13L@tohX7;}Y=L#Yo^2T`mAc zV=@A@7`0I>z_04uI@kpGRuSOztA_E(=q&u?202+J13aOxM8B@n8*=BZTLc_LP8;YN z;J8Y9bhp2&*56BCFNa3bj=TMy73Uh@bnU5F{}f&?6=c~l-NUkgt^w`_eew_L0DJ5W zyR!bic+L3xB?8{TfEKO>juCLb&3kUM-$#Kq%XZNL3)=P6BaubZbuyjQ@T);@CbO7hM2u$i=3=?0$18f$BP&KKa-=_&@s~a_u^P4}~c-sOGN`cLUG20#+OFz`dFpaJ>Jc08IcN?f*1C z%yj`zO~U5-(oBp*VouAlN)FRu=E0bqs4NR7ITg`JG)KqN}Und}Kx!(}(C_Y8Vq= z6L{qtHj{#ST8imR0nC(d(aJW=CQG}7~(ZhS^31<++ z2|btrrR{qdV5DoSTWG4?@2jWW*amZWBewwhG9H6WLFj>*K1>5H8IJReP05{5-p^D* zHZAkSZ-EbB;y5C&pHtWA6ACzbA}=I&M0658G8sP&O(|XtVL$$w$wn5hI4|ol$VZZT zicU{eO|`QG=luR@<{@=YAH!LTL{HH7tgMEQwXPo5+{Lh=o``+W|M#7@J zBEQ_iweCvM5Ts`9X1lm1kJauier(iZK+h+)d~X-%A&!Fwpxnbg=?%+~b9-Z3z%vDU z_|$eyt%#-+^vE37_Z&kuu`nCK6iuZ`D$+m`6xd=4N71|X*mf>8-)PrZ)6i5#J@Es< zkPSUa=)2JrG9fi}mQ{K<=rJxXnHZ)6`;UY9$uUU17nI!)MfC7WjMrjHwR?)L5|z{% zz`@$ozKGwD*S%y*OkOCvH_wd-x}d(YphsL++occ^MxWe{Nck-idJO1xn+Q2^KUH?X zM4Rrvb+cC%^uVBdNr@81(enwf$M`ZlMyy1R;`Moy-V5{zy9dt{iwmR9`DDFue4?Wq zrV7-KfZ&5}gJ{}ijB2GP>efpj{c&KsAmpimYJT|e=ik|c$cDLA+zPL&$ zCv9d)hi>7YMBXLrPy=^A?m50AT?nT@kFA+6g$K)H zJLj^ZN4;m=>PKNlL{p(hyROZhU$C*Ud$&#s>8fe_-MdD%yH_kOP^OLp|N1lxlkZa* zr^}{YT*hX;7#eKP)H@e3jiWHYadmS|P0M>kGs_(Bl+V=Y8B2aq5Pdj$(uyXLFsr=0 z5zh5Y&3j_Ehk;p6q7c;w)jnSXJ)$z+tfTPHcm6>S4GhgVa`(6^V^{k3?VWKJF53kj|Ja+U5}_Z4G+i5%-Opr@9~W!|~&D_+E7+qpFK)Ka-D zJJsG+Bp%4OZT?m2plI~R``^-k3$yQcbDQ@cFSl&*tP z+NUh86c7B$Z|I}h`EEZ}7X%vVsV1^?jV&lm3H0Oyo%@WRa`beRb?$kXAA zb;W}y-_i(f^|71cFWlaB&v)O3;itOS-3sV&lU`pryBK0yw=js?du-^5dqcNB@!hE2 zMM>UNJIiCq(gqd}^AaE1m6HUstnu`0Zz8AXo>WVQC^buwXPu#}M0{p|j9O+T);tec zfH9ruljsdfV`n@uz+HH3=pnCCo-e2FNgyn)F~2GaV~fXb&%3x5-l@J9CzQH4yG?+Kp+~fFpD|>0`|h8&DEMp5ZHs00o(-9^)}gz%dtMw( zcub$ylRlMw^*n6(OB*Yl~ z<`8!o379%|N+ZbWDKzy_<>ObHIu@2{W5=bDGofSoowZL65ZirzNq~dlFwq=c$-wsX zj)kxZ6W!lR)L=bq?-Z+Hu60naQElR?0(_~V7vLTNCW#T3|f2%$bvS^=I>QdwbM%(`k3yS+aiYlgW}y-tn60DZx%JV*uuw5*)!P7+`4Lr<`^uKa1E@@)3VB zaAZN2i0xBK-@wtgP3A@nuGIqtHS+7#(CZD^$hk)2Ctm0CV7n~|n5&;b zYv9CgjLF8^mJoJ`9V(#5t5fo0i-y*vEw*tK25tlI3pRLWpM#S|5uBqeXHXe9JQG7# zsi_2fO@j|1M0}lVm21vb8Fq%Q)AylO%d8c}Ed)bWlBk(7lCc4tfZKFA>DpavVY2Al z5`zw04PDA>=SYJvugv0MIPv>9QCxPeyiKhia~Qy(JGA>^?R2%8dtvZ(A_O3gi)Wj+Sti+Wdk_-MQEl4XH~i8Y{2*{M9Nw< zIHISc&LPGHRl+~R7RqBk%9x=vU3~rocfN?ikx1T%nieIXET^N&m6d{Xf`Q%@h1auq^HV)XKr`7iFXp$659{4(%)2*mX^FJ1_#QA16fksZ98YMFQf>NX^ z^+YtilOK22DV3j-QANVIR}08ajyGCo2N#S~XI_x95AS)iSlkV#*>aZjA<3ky*nd~n zm6?dtoWlUVuyf1+&LSRzEsb91hZHAa{}g$NdkoN1%&6j8D&!E%uccESedNopCN##p@$$gh))FiVWo&hZ#OpCX zT}>_aKENn2=hvxs^WulJU1vySr#*2JHP0-!S&toI6HzDRN;n=-HB9Na#6uQY$C;At-BFSB)}PXG^X16NTOr)qk;rq-#KO0a z0&erWLg5v@My;F&;})lC<7}&3wj?&!WAJWMKALgqjbN*3MeYCw92rtg@H@i_5(P%6 z9+UNMd0=1Q0c4%-zd7ifo&NIisC~HK)jpQvava7U?BNEhXE`oqy`>FuNe|)jD5ys7U==y%Q9G86$mZ~vWUpX#$J$X=bg?HVD&i3;cJC4(Whn@JY0}3Pv^LUEy zDSP2MKUGQA`7SQ+7E(KjGgvn24C5}zl3R+)@x=Gd$pvk=#2WWBGeNBfkaciz6ebiB zGN1Of5|=zHa{jbM8p}b@3wxj3sF||Jm}gNY3m$XOJS>uGKZwV0t3&x{#${DZ&bu0b zvt%S`NY08fRY5utR5LcKaA}!?hZwE1&SNo}h>vDm#ub?Z9%8^;#)r!ktaM z=IvT>7ff&9yqk|folfn>O*!bU33qmNnzw7k-GkAmOVwwkSP^%2^_jQJ5%rK&=2CT8 zDQ3o)KJ9D8&NkBTGVaW(P&K8wJe>$^SpLu=(mTcAOl;C);si^^+8_xoAE zUcq0Vx`YqB=-Fke4T|r#O!-b1vif~_V@(huP~kZAOaU+M>NAUslEYo3V@*b zlZb3dmby1=SCmq2muJQ}ig)BNp0AaiDG4COJ z;!P38@gaNFv0PNi#E-d!2*aN32fgTeoGmkc`ka0Sxn6$%qEoq$2T$yIB&HM(Jqp^C zM*>Sv;puZz#PWG4xVTXwPzpR?yvsLkfReLrufAdOHKUlAz>$0W#KiS4nz1(sK>EhG z#yvMmybJi4xS+7?P9k(ofO27gg(qVkt?p$1P%HVRP$0%uz=MqE!QFzoYT11m>O;8y z@N1h~I*b(`wIAGTjIrU8^q54j|L~u8_qowyup%B%{kjUHew;@Ht_vEz7&&?JfZU>u z`YaoD+7P zIVKEvusXlpRgX6f=+xV-@V2I>TM-Z40?r<;_u7pfGMeNEWiQiMWy&YhxU0oY^1ePM z{@}-%5XDE%Gd&jZph!`|KAOkOmmOXGN5#Vm8hxGf@KOzA$41Urp3k_!B`opC!=w5b z!>xEIIe7v5P>8bf+bUXH;+>6+0U&L1j^SRiSS)FZwT&cl-Hj3aCMXQF91;cj)>fX#?)^0lS~+U}t@0rQ5{}FLCI*D{475x^6};$HK2G zICs3i{yARwYfa!kublqK3)aOODZspve~4;f&jb8uw?#Ir_a6|Nm6d<6qcCxI*e~IP zc11dfnw*1FnM@LX_h9ThJr~`$kb3fWQILh^?^txDp@XKB;^{%F*T7NPTO4+8i1(dR z*L7?nyQ-q&Ox~mDA#Px4zUFhfj;*Ee;$ejj*DKk0bF0NYWqYeH|-vKsT{J&@H4y z@0ATEK&{!>0Eux+08b+vkOuB=007M-+)%4Gky@M7LDP$47~NWnM)rO@h|mWV#&nPX zAc2c+qE5t5ze#%TBkI8@N?`&J`mPVSRDyR zt|S^cr$)JcFT5yDD0MN2xZ#Lj)QZzMWqmJ(>C0gV0_7{6D6l2W=^@+NY{CCfxbsk$`yH@w zJ{gU*Xk8Z}DsaLuL_q~4?}~whnygE)1>RsJuP)V@!9^4xp}}3sAi5xBBZEelh{FB^c?tPS5Xd~zB3j~k!TIc0_g_06bxYChv$xu zJZ=o=Klgk{W67!rHUcjO5yJJ7RM!B3U)8ULx<8%@NI}jFbYm_xn&6<60n>cAaDb=%+Obtc45t z=Sbur|G3#wh+N^}4(fFh%cUSzvfQu~1^|*8=5{~rsUDlDu{!^lZtvWor_jiW_vML)Kk+?#mTr2J9{TVEBN5LtCJtRri}?Rovg}f z_Oqa;7)Ub;N&I|I?R>q4?~c!Z2Ru-#;ACvBI!L28#TLmewrnYO>vo%)jMi zp4!Mk9=XI|hG@nC+#^fnJfi!owF&TiKw=EsE50T&$tc}OTh18tN4FJ5gN=_H=YBZY zXw#c!b`lItfk@PR3%gxwn1IBGL&UHTIK+2nofd0@^fnMk%T^ApuWT7224i`EL0s>{ z*)Rh3l!3(sFBPWWJ&-ofqwp$rH+EqsY9H;LwwqhPSzG*eYq5yr&XVw*8R=y|6%}b{b zu?6^KLkASGa~DLu4|sG4XolbJ#Rb+@rtix8G1TfDaiT@7s`}Z?oV1GaH0flRP9rF3-QE6j-o6Mm=AI=3JP~_f`0-RJ7 z-3Wzzv`!x(pM}9-DihR0&z;_<$vDfSH;%lJ*4QKf3L1n`7?mx;q|rSr-Igjv()FCh zCBy^@Xw=M!{zd`^VQB+Zi#XW`$+r#q-r(@XMA=`(finhmAVFLJloykTXj)NWvbFe< zuf-3?0ftAf6u=0>u3VKid{G^sObjNB~b4eExHby2&yUq9k+SeU=Hux;WMD#l(lrq1DxgM!ZvE ziC1f}{nntR*Rt$Ntoe_$CEOV-#W;K^>jRO+_j=AQ-qY*y#i%r)e#}+uhiEe>R1u%| z_*dLhIoXL@3p8Fu#^p?$uW?oN$4d`=pczpwTR^yKTJex{e!hA>fJVGM?b!7n~3CAEaZHS^D zg0Dg$?rS6Fcm@LzR zwcz$X38U7n>|}XMT*hFZx1Mp$3FKO#*tVrK)D^)GVVYwum8eS^N-HHrW-bv~|C)0( zO^!}aRdE&_u0SF=2%Qgghavz0vVBsr(OaEU(m8PCNt|YmL8~D}XXN{sI~o&e)FILy z(#A~^1WYX35quNRSVvCaT?jP}r{-DfvuF!MJ0#!;X<6)|@K#VsFKPVM|D5i>+i!RF$%^z3-DszOW35(;TiXP-pDC)iTzXRcfLV)E_7*#TGjN zPUJfMby^;2w{o5`CK0=?SpG3jZTH*iS7~vVq(50$*rngWrUr}?`RDjL@&?0%{Z|kE z{BIOMp8>3)(}#)YbO}4}h&AN91{b1Zy=z!(@xGo$MYPdP3Tc{*XLq8 zK`#^q@nxO;DE3d76t;>58sbx9?Y~#ZzU*An+UF+|Ud)fD+|NCCobd3fuyj4T)tHKn zqKVeZFQNf%bwr0`7_3~fq7-wIm6iWN&Iye3>gq~mnLaNPZ@fF$-9PH=Hv!CltgO5b zDCeSjA%O_{v8`t> zUcA_R`ttdUZMM1f?B&y~|6rSo2E_DCkYoMFCc?N?RxS#|r0jccP;8+<-rl36_(IJO zb-O_rUGK0x!C&Ded0fOh?1uX_&`rXfEKtb`_gbM+J1jkdDV~$=JZo*WUdYXCYr7de zHMf}Fwy527T4QIs^|bY^+Q;^{S|>6ULsiqV+zzynZ%s1K9l`XGd9FBPPq6$QP}||> zIQ>cEUQxAxot1RZq+~#&nN@s8oc@^(wl=gQEU1<^gP7~=y4nJ-q(%>0(C5}?=s&%X zApuu}4v*pu;vtDhX|1tox!a;B1_&cp8GeKW{XoOqUmJD=5C!_QQ$#4kj(A)f*p^6( zaC#9)A=t*X$YUF?D~GT{*tVtZk#rnrn|8hcL>4IEW6D{)3mjs;MvCp!S{a6jMX4Sr z$J;Aj4+m=avcXR$HeluVKE=v8zBd{uBt@T_+uNI;x1T<1jf26;C-<6sj&b%f@^7Zt z!AT9{Kl~NcDupvsHq%nJSm(k;8#J<06jWk%>Jm2kXgioheg?Evd0ok`L2)vMz0Kvz zu#yrD3ZAX)`@X+DKIKnF{Hdn6@e%kQCWUGx3{btn zbIDieScALI4$F$IoVv**5}q}5P^IooX+&$@@SlG+g{LcPWCkHB)nH93fF^r?(#Ha{ z9rCZSEVSD9dI@XLFLa4$u-1>PcKm@1jPVS3u-R zd^GWHi3CP3))LlbEQpdjAW!;2tmd&uJ}#{bKw?0Rx7KWp(m>>B{n*%iL;TUG)rde@ zDNV^hkQs}M(V~K9=A~qWrQ!@%Pv|$>dBq!)Z{@i0LnUTdT1{9Cvq*T>>S+u;tS_r; zM_O1{B1`|Ef=(x9hN7+P;|(4?$x0Df#-F(B?1_ifpn&JAP3d@fg|;i41Mz!x96@6j ztns(F`L})x{yTJ?J{r8>?^b--gMS(d`k)i#M*%(hh_yAN!J8i(#RBaRMCI_d3{CzH z3J`0oH)I*}LJdb_v?b!9&y_=FEi~t1bS0@sjnHd%AkJg(<}GPdtx}Hd_#U2Y@!=>` zDB8*h(fCDXo z6=Lj0gpjZIxyuH$$3)n<`^UsV_i`s-Tl|(Gw=Pp7;$!jff3CSY^J9_wxvNo6M$3ek4QTrz7)ys?xM+&Ja3TE|>^6Q5SyKUkyL(2K045*{xnS+ZK@h>UB$ zHhvX-@scDS)~rjc+ya@2rtH#UgeUYD>h0D`&!MZWeKBmB z55-i|N(oSTCZgUA19S(LQ3zmHJ6w5>;@*yB2jB%UZW-1)3lCFk07(!H)_D5HYgF-c zLo13fM~r+^MP%d-+|P$j(i^hI8yGG3^Opr+^E(7a>|w~;^J}(+)=sDddzu%sH{QTG zzfMTT)H`{_o28qOjSrj6?^?h8J#8DLT|ab^7ti3Y(Zs*tt#akBK#Ky1b{u2c?!)v{ zAKl8(4*{<(AaJB;^|%{+z@b9 z@HkNg}06pWd)R-nFc;F`Qu=#_)FvAU_jU|IZC1=3yPi`uC4FIEwJI2YTe^HOT z3_XyYJ)fV*k^2Sutq{W%7?q8gdTWW09ob%7Syl{Et0Jq3iJPXXI#25F5~iDCV1e)4xP{Jt?P_g2X7FjzHPW{2Y4Y^XiIfCoNN=C#1^qZBia9cIcklv zGAdhni;=0t)h72dDtBH&amh4RLHl53b9t7PjX51MYr1@iBkzTd@5Vj1!~mjwvV&%2UhpdXCc3bstEUFSu58> zsmPg}of(Z>d%PZin4prx(GTtS!hVGkVZueReZS#wR z=@nSP@yXZ%0uLTxeR8kS^Vwj8T;+NYy^hz`SLnjWl^2r-zO*yqO{WMZE*$Z;4Tmnk zTWJcu2fK5blXf#N9JZDl7~ z^ygZOeD!h?JhhP*)`PzLnSW{h064i}=P#c1-JUK}tCK5V;_%!4c<3BWMtn4RFRIa= zuTXC-G&n&tKq6xen4X4$xMqsDHCCK0Z5-KMfJR%{c+=>dUUpmHKO40Wswi36@-xk@ z418KW&8Cp5wz)1VYevECPNjM%U6aigG+j#aic0lj6~2%PxwV0%zwahARId5hHYmSg`3zx9gMK)wr4(? z6H=#y4+9buXjIaYh736_m-MCc11e&gIH@x8yz&mEerlf{9UQ&cVgLN`FlU|+y`liX zC4*db5u!{KThGVYNHx)Xj3=nN!^!#Z;Ar)b50Mlsk}Aj&w85UO1DR|;J6v1I5y6yW z(p78Ym=wKr0b^?aY#q}J7!)Xn#W6`z_|iVI5BV|qe=AoYhf7UQ$VOYP?~8OfcFb)#i~gc5)VRUYCDs$tW6`dj`K-%`?p@XRcIB*fkzu?6Bjp z^T)(pk+W1(H*)AV=y5HsMYyKf>2)}PEOmJ_vIelqpI>?# zy|<4^{=@4%{qrW*96@osW%+OG>C3Hx{I~i1QT|)TvmE&kx9jx65>VIPYkdRd?^2rs zZJNdnGc8iNXJV605z40CzG>JSTc;w(Rc0f<3wt5MTMsP^Sri2>`YL^2w~(y|H&eSy`7Ywa0B;ko?jM?~Xhs{x3tgE-DsG1^<8ka(laY|NG@5|6j&) z%lzM>^$>mi+iLWRY%Q0X!h)-~HJ#|HH#2d9Sq> zp32VZ6REY}k%$*BrxifJ3|{_OdF&4v4-7vVdr1>U zWXH}42Dzfh_i76{3uUbl*TrA?D%Vm>tSSB|@`G_;S}py4oAiCDu^3IA1ruLIN;(0B zbs(cw$h)til$;&$x!w%Pb8q7;=`G!bnGz7qFk6Lz-ujH$Cs0|6kwf5V1?OZ`N~`7E z$-tkUjKjVV&K4jiWsModKciqIGt-nxnln{Mqb4fjVw^p zOmC~#(K*+u1{avkirmL{VO-17xj!u@TY%OqM^$Oyv`7fKAvpa$rR#8f0rvuSow{93 zy%pUTL#))O>rKJ$;W;6MHzR*(vP-R+8oJUwlQ5}lj`MD!Q(0|Z=(1HcE+p8wdW$8* zv=a*f=Ace-3V+aD-Bk4%+eP9E5a`A)K%4a>-_)7+V(px8SsUHG$Qiq5-pIvxO=)HA zbsc~#?aT<(5bp`1!Nua@QYtz~HGR^BP{E;>CFp0Kd+>KUw;2m{m_D zyR4p2g@jo*OZE6~>pgTY__S(6yhBHDi#8|LIFoxJK_ElJkwM6jLFgAG-~dR6X_793 z+l1jeuP7o}>jtWPJZ~n_8+Q`C>oK^V&lN>)tBStl4ke~hS^xMf;&h^VI=?L#vJ`JV zsID%T)oG5s^ty^WKrTa{(TJAoIIYV}_PktSH?8_A=yk({wvA6 z(lQ$=@eUK^tLtdVJ z<$6=mX-eJFvX0u(T;T}IiUZy3<4xPGIan=PND4wqW=M>saEU4&Up-k$9zAlW(u|A{ zT8F==fP*BgkUuSw#9I_>N>hu04;_bU14oG#EYPV9anOXj(kG0EQc?;-aix+%NjYA` zHj=6{3T&`bINMX-&PUE#`!lTH9Jop7yHTDSD=+5*R&9NLSmWN%?N6Y^BauT%a3uA$ z?g*z<(B$$u{ej#SM(FJ-WA$ex3!ikcsZeqHzQaCjLX(I)+alib9*@Y1wF2g;LW>T} z08f%CrYqNSI(yJy`pOQ-La1&W`r+WZGmb!@g?3{Q>+rl;r9n%Pc`4G?^8!YNNz(!% z_cswJgC{P~0b(x<`s&EaifMFIvq2kNinNLRZC57YGv_@r#k^OA(h`c4MT)uO@+H&^ zAL5%LzUojGB-mv@qi$K!r?{B2FfEcMg*M+-we zOy6?`0f>HH?+}v|7>3IzFNPEJ=UZX9jzpVQAz`}+bv|HQ2AJ{+r)T5b3pV0m?rpR8 zIrrv9`(5O<Euxtc>{k5Z8 z3GziR;x|z^8AHKYVGRs(F#6Q@BGw$&X>$Yh{lZX3VV}L++?=(63rzG-qb_TXkiN(8 zV+;OvKBs@9qkof<1U6c6;EdxTCL~o~`>=NYegTd3F&N;lUxW-9`|L!6SkOlEw2~rg zQ(K|sTO+)>L}mFHen;M zH@%UJ#<{QYbm9t zvf!8{ZCMa{cKW_^Rtu$eWT0SCsNjOM%o@9xqA2(};kpRMeuu^7_HenM>>p~`wEvTx z4Pgm5FkIv~PJ)r(uP~^*AtS7pWWt<|2=vIev6Ym)c^=Ix)(rGvTCDLpv|ovZ)+p|eIQ&X+%h`Q6G5F3p19;V&Og zj`uzuwGa1meUTX((}3(0fVl8nzkljp6u+PkwdS>6EI^gHkR{!a$XCm>C*~sQ&XQ|O zD>HQ@-Xg+EIYfOF^6UUWEfzGUUYQmVv5>Q@wPsS$iPP?w0t#2t%x%rWv|*de=Af|c z-PIW=#GbCkzdTX_f!bep~i{G`H&3NcwrgeJ*C40^? zH05ds#M}XRzTg~DWR+H{T?ne(6ph}1M{WXZ=#%aZC|(k`lezX?pWCDj2NHtIN2`YHCaw?Eo!c&^O>(02KZu| zD7Bz8lxD4qUAOP%$(f6hPo%`bUj09|+WKa#x%tE{CQ8Yx<0h0Y5*@yn6X$y>ib^Of zk#do37jl>Q*xBFT+dqAE?dG|FQS`%M9tZw4?|YMHZgM}ny@|-%g!f>Q$(Zrz?@WZw zt^?7*nFbpOg-$u7_p)i5334Clc1HG z`8xYi9R~_LAXupP1f4ic>pG!2)kcGO(I8F@Qy#I8X1nE??W~H>>w`03Ne3VI_Fun$ zW0E3;TXz&Vpu1t3K~yd;93ZfUmQIJGzwe$;1{G^%%{((EId}IR=oy9 zw5sZ+krj5VZJLB%WVo@oP*W5BTNKapWXCJ=J7_6==?Sw6xmI(j$@B3MioKIeU(z|* zC}->YlRe$61<+@KClk z%^tl!{CIZ!!~T(dbre?IWJFh4;`+Vpwa?nS`$uOrMj?Aa)bw181XbPq{?Xpa@xjsA z#|}ssyKigvAtPROK%(hc57G>Z_`BoX_SwPlk-4W?tZ}85mwCFg*%HOpi0M$do@-)) z6p0Jv!NP%^b)70wAI|sD@!men&+h3jC-xCoSa4bI7Sb!X@t8X4`5sW&EaTgKcK{?^ zIXJ$HemP6jqdoA^9{7c3UDAs%nj`rz1FH~Npb~lJkt&&*7cf#9<V=kCyb5v8{KR!G>ID2>e=HuQ$HS`s| z%{3BQ_xr)+k4{uzC-a!mw05U6XB&(>#XY`c7`unX3Fyf{YesRvYJHexw9tK-#bNqs z5rSA@OkBjd{Q=c5qm)^gwS?z2tju}#!0+XImmoIA@ktb(=P2D*Kh0u3TPl5yEf>kvnFMBPRhk*;(WS zF`algJ$%8>=5&pZVjMXBp6ff;rc1v%P7I~#y< ze4eAS9ORZ<@+xZel$ZFFdauhCr}tvlYCh25j$i(I#1zVNappM|ZIZ5%7f6*oheh+%27 z{5WGqwOUQ4IIU2b<_i=$7G~bF$(WWBx27N11i;%AVeOsFF1b|8wz1cDd(Psnx~J{U zbjh7loZ)J@h0s!OG@U#RUR!z58+G2)mXMRcXEu}A;wW$hU57DFz5Z>GLSNa{fP#|1 zq=14O#SYtk{=y7K(NaUJWzUOp@Oc00k}vlTd%n4O=uJUGm3D+m8$#7Q0`*n&_N0RB z%UDH<5cX$bj0FRf)gEQ&%xHY79ILoXyQ<`KqlUy4^;$XI*4V0wvplS@Bv)zFwi0X8 zaS@xPtJ2CHjq>@uYcxmfi<@r^;7=K4E!^hU6zldX^yeQvwWVI#xF|bv;Hv? zST#}s)=2J{9X!>@(WxiKDmim-%&GDUX*=^t`z%eAL7?Y&8>@aK4#}6e8)?EK){2~9 z;F|l*D(Tx>lSEp(RV%mFGsR9*ZvL2HXviFUNy12)=1d^rMwsu{H0z?BRu8i`r3^x8 zsbOlKH3oio?)ce9{@chgW;$nTS_!&%;`DD%)pvxZZVZHWBvHd_L%a&%Y?+u__DW%1n}kg-#53Py?k28|Ni1J|NBxNT*6Dr{|-`~$l}g( z$eJ^_-(F(u`<(P$(QHKq-j>9UwsGoR6w)$dYfsej3rNU5BRs}L?eizE*G6Yi^6csA z#l7l-lO1*x1{k(yJUNHa4v)eFul8KG`+eB!DPOaQ(5Rd?np||bjUC-=#S7pz16Q2! zWCe>}qiMx0Lq%Dvc#bBjZGHEhp_(jKbov|T&z$6}j*DJ!YKy!DBC9NS@B)Y`Ys|Q4 zjKmX{=Hjg{A=k(P|LnntJf{6W;3uPF@>N)X08p|2!>5<|{r|<*^T+*v8P6^6|2dUW zP!{)sQLRi|FgQ{_YqEo3!|pj_hvG7Ng@&`5!V+(kG%96^`!6Xj?;2H`I);`TKvm8W}y@v|+V>e`>^NY4P9w0$Y<1$-lx~5ZN{hHeF3&vH0-&F~Z z%j#Wd!ECfB1LQVPQ{}sqdr`eJ z2=QhiTSH0n)9efX4#yiN{U5d$;ry`(>wl&G|MdB@=eqvC_44J5NBw^(&vNvCV`S#Y z1!_Cub1OpLr<@5;PQ?Wr2T(6P2U{Pw!ERfPR^M|P?CFc=X))QZNhLKJ z4X9QGyx)o4Be_S~Yirhn6gIBf?1du`mDCMD|BxY!?6pTzgDK7S9le3uI)>pV5I#*U z>G-6ur|xs#wEh<3|KZ8w-%w*#$5lkBj-@r#4Q~;x6reF7Yj&t+YpvmjJ;%qpAPv^o z+J4#Eg#T?d6f#Xo$c+*~;2m!excyyxk(CQPUzrP}cw7-s+j440?tn&9nz~*ejb_U* zn&Bmv9rkH*4r-5^ptog+`}T^Q8j;huG{J`-hMy*5HGd89VRbS^CXX~jazpqr|0qt| zki=ReS>A};y@LEwLxK){rxMF@Bf-UR_56Z#hVcgzU5AJU^q{yRE8+wZiJ&!zIt z6i>PQx3%@+#j~9J2c=&;%74pvcHg#7&lqOF;ij71p##fFGIIX)$NkgJ!SRt=Kx_|K zrhRf^D$yQ~l|oY962vC+4B)bb4Qh4CIf&L+GZO*D_l_oU+93)Sl#+?Vu=33}?1VOa zc8-Y$6SO0-t086*$E18c#3kk>Ja;i~VBa14;kDam-+WUzTeIIrJWCrII@ppMWDS7` znN?&XtVuHe81JFQP~p-QHN2QN=!l%r@}79wW_amFSCN+_@b{IM40l#mzQ_%Au~{zl zVmkG?@&+auQA09qctOV!?EG4!S%e8Bl;Eb_OMDQ^5?-R&OhuuZ($Zglv8Io~99p^r z-~}ib_1ssu4o|rxnJ(s)Hu<{p6qjM2U568P8YO2XEQ>S_BH3_YD-bu$O=8H&m#+@oppOv$a=o4rp%CeLKPB^9wZ~)PuSkBExP*tp@yj*4Cw+%|0 z*0AgkdAVw3cj)#$shE@zQ-OQ|!a%OG9Qk_5Y$W_-^%fT_7J60M1h2h5-CAeOko_Kq z0bc)p_4S`E4}`n;!{%=-Ei2t$U$5n(&eFt-lU7`mH2(_K3xrQ%q;4eLm6fd)JDmi~ zQHmjVNrn!Q53Q6*OzCd10XitrrU&M1HjLI>HR5yz`iMsGpCq{kV!&T6v6fd~AGxq| zea(JFpqfoLxHKeveD$RGj@RH}ns7f_Ep>BQW2-d}v5?H0O|Iy^YSf?(F~V1L`UK^< z+93x-(Pu2zR+X4G8CTwJv0Xs|e4=Ts{ow=CROgSsX-?E5J~c@pHaTV{yw!ESS-BnD zt{jC4D9;@aFF$BLM<{8DCf8Yr*^+6!BlG4PJI)F4ee_451j@JtoTIF!t(tTt5Tfie zXrxmvcqbJh#byiA4JN|IQ`tsg zdq9H&%?V30KvVL+gOgV}hYaX0&1@`E;aUxaYl$qLjocjCuijkP>Pm?9?!(;}cj zHzmGFV)ubS)L#uIFcjA%>$N#o&l7=n@dpl9p%!gA--S;9wd3P95tUCV>ij$GAYmit znkCLBplf#FUa^rE00n^%6GlWvYKshD0Aj>7aOrqHE`(NgO!yNXN6O6NxIL3jz`j=J z1;msY=jN&|w!c;>wzGfw@tH{)_Z9rg(_OZS zT#K%w(LbEMqdA=u{~V;0A;d;PyV51ToPH!S5zS2?mMaJ3#hEY)Q?XGOs}*8wm6*%^ zYd-X)*7r)!`4#iAvhwGONmB(ZqBskRDsXT3+ZXt&M79eY^6;ucdh-uHi zUyd4Lv6a9#3j28M*+0OMXFz)Lr{;>v(Nl#!;{+ApU!?udWd9xa&lhv>FP^IVf7@GI z1^e&Q?MM6XGM?r7|08AbbN~D?W0iyd*y7nQB1VM$Jui+X6zS*nq(877R6hFr-;Lu- z)#pD)A0TnIr8vp!sc-aq^Ty()ur12{Nvh<0ejlVlD-2KzgpzMnoF?j(ih$^CW-%$Z zEMR^4yAJV}zUXj2&PX5fW7_|P1ncyA7_MY~QJ`Y~-+KOhGk^c@<>t%B{eKzHa`%5h zS#3dCW>Fv?CqR_0+R#%mUhm}Sq3r9$5C9U5cOwA6bkN+X>;KLKrU-%8Def%%OF36kI&=t_}ua5{{z4Xdt(5W1ORotbJG9- literal 0 HcmV?d00001 diff --git a/charts/cadence/charts/mysql-12.3.5.tgz b/charts/cadence/charts/mysql-12.3.5.tgz new file mode 100644 index 0000000000000000000000000000000000000000..1d232f768e7e29e66213059ef4a26f302048f70b GIT binary patch literal 67964 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 LfYy#40F(y+lt!~T literal 0 HcmV?d00001 diff --git a/charts/cadence/charts/postgresql-16.7.13.tgz b/charts/cadence/charts/postgresql-16.7.13.tgz new file mode 100644 index 0000000000000000000000000000000000000000..d5bb1015e8b7f4f8d9ef97ece11a10a0ef66f8a4 GIT binary patch literal 85046 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 Jv6lea2>?=6q)Y$+ literal 0 HcmV?d00001 diff --git a/charts/cadence/values.yaml b/charts/cadence/values.yaml index 863d220..f23b273 100644 --- a/charts/cadence/values.yaml +++ b/charts/cadence/values.yaml @@ -717,7 +717,7 @@ config: # Common SQL configuration (applies to both MySQL and PostgreSQL) sql: # -- Database host. Can reference Kubernetes services - hosts: "mysql-service.mysql-namespace.svc.cluster.local" + hosts: "cadence-mysql.cadence.svc.cluster.local" # -- Database port (will use driver default if not specified) port: null # -- Database name for main data @@ -1205,4 +1205,268 @@ schema: image: repository: alpine/curl tag: "latest" - pullPolicy: IfNotPresent \ No newline at end of file + pullPolicy: IfNotPresent + +################################################################## +#################### DATABASE CONFIGURATION ###################### +################################################################## +# Database deployment configuration +# Enable only if you want to deploy the database components from this chart +# For production environments, consider using external managed databases + +# Cassandra database configuration +# ref: https://github.com/bitnami/charts/tree/main/bitnami/cassandra +# More documentation: https://artifacthub.io/packages/helm/bitnami/cassandra +cassandra: + # -- Enable Cassandra database deployment + enabled: true + # -- Number of Cassandra replicas + replicaCount: 1 + + # Database user configuration + dbUser: + # -- Cassandra username + user: cassandra + # -- Force password setting + forcePassword: false + # -- Cassandra password (leave empty for auto-generation) + password: "cassandra" + + # Cassandra cluster configuration + cluster: + # -- Cassandra cluster name + name: cassandra + # -- Number of seed nodes + seedCount: 1 + # -- Number of tokens per node + numTokens: 256 + # -- Datacenter name + datacenter: dc1 + # -- Rack name + rack: rack1 + # -- Endpoint snitch strategy + endpointSnitch: SimpleSnitch + + # JVM configuration + jvm: + # -- Additional JVM options + extraOpts: "" + # -- Maximum heap size (auto-calculated if empty) + maxHeapSize: "" + # -- New generation heap size (auto-calculated if empty) + newHeapSize: "" + + # -- Cassandra resource allocation + resources: {} + # limits: + # cpu: "500m" + # memory: "1Gi" + # requests: + # cpu: "500m" + # memory: "1Gi" + + # Persistence configuration + persistence: + # -- Enable persistent storage + enabled: false + # -- Use existing persistent volume claim + existingClaim: "" + # -- Storage class for data volume + storageClass: "" + # -- Storage class for commit log volume + commitStorageClass: "" + # -- Persistent volume annotations + annotations: {} + # -- Persistent volume access modes + accessModes: + - ReadWriteOnce + # -- Data volume size + size: 8Gi + # -- Commit log volume size + commitLogsize: 2Gi + + # TLS configuration + tls: + # -- Internode encryption level (none, all, dc, rack) + internodeEncryption: none + # -- Enable client-to-node encryption + clientEncryption: false + # -- Auto-generate TLS certificates + autoGenerated: false + # -- Existing secret with TLS certificates + existingSecret: "" + # -- Secret containing TLS passwords + passwordsSecret: "" + # -- Keystore password + keystorePassword: "" + # -- Truststore password + truststorePassword: "" + # -- Secret containing TLS certificates + certificatesSecret: "" + # -- TLS encryption secret name + tlsEncryptionSecretName: "" + # -- Resource preset for TLS containers + resourcesPreset: "nano" + +# PostgreSQL database configuration +# ref: https://github.com/bitnami/charts/tree/main/bitnami/postgresql +# More documentation: https://artifacthub.io/packages/helm/bitnami/postgresql +postgresql: + # -- Enable PostgreSQL database deployment + enabled: false + + # Authentication configuration + auth: + # -- Enable postgres admin user + enablePostgresUser: true + # -- Password for postgres admin user (leave empty for auto-generation) + postgresPassword: "" + # -- Custom username to create + username: "" + # -- Password for custom user (leave empty for auto-generation) + password: "" + # -- Custom database name to create + database: "" + + # Primary database configuration + primary: + # -- Primary database resource allocation + # resources: + # requests: + # cpu: 2 + # memory: 512Mi + # limits: + # cpu: 3 + # memory: 1024Mi + + # Persistence configuration + persistence: + # -- Enable PostgreSQL primary data persistence using PVC + enabled: true + # -- Volume name to assign + volumeName: "data" + # -- Use existing persistent volume claim + existingClaim: "" + # -- Volume mount path + mountPath: /bitnami/postgresql + # -- Volume subdirectory to mount + subPath: "" + # -- Storage class for PostgreSQL primary data volume + storageClass: "" + # -- PVC access modes for PostgreSQL volume + accessModes: + - ReadWriteOnce + # -- PVC storage request size + size: 8Gi + # -- PVC annotations + annotations: {} + # -- PVC labels + labels: {} + # -- Selector to match existing Persistent Volume + selector: {} + # -- Custom PVC data source + dataSource: {} + + # TLS configuration + tls: + # -- Enable TLS traffic support + enabled: false + # -- Auto-generate self-signed TLS certificates + autoGenerated: false + # -- Use server's TLS cipher preferences over client's + preferServerCiphers: true + # -- Secret containing TLS certificates + certificatesSecret: "" + # -- Certificate filename + certFilename: "" + # -- Certificate key filename + certKeyFilename: "" + # -- CA certificate filename for client authentication + certCAFilename: "" + # -- Certificate Revocation List filename + crlFilename: "" + +# MySQL database configuration +# ref: https://github.com/bitnami/charts/tree/main/bitnami/mysql +# More documentation: https://artifacthub.io/packages/helm/bitnami/mysql +mysql: + # -- Enable MySQL database deployment + enabled: false + + # Authentication configuration + auth: + # -- Password for root user (leave empty for auto-generation) + rootPassword: "" + # -- Create the configured database + createDatabase: true + # -- Database name to create + database: "my_database" + # -- Custom username to create + username: "" + # -- Password for custom user (leave empty for auto-generation) + password: "" + # -- MySQL replication username + replicationUser: replicator + # -- MySQL replication user password (leave empty for auto-generation) + replicationPassword: "" + # -- Use existing secret for passwords + # Secret must contain keys: mysql-root-password, mysql-replication-password, mysql-password + existingSecret: "" + # -- Mount credentials as files instead of environment variables + usePasswordFiles: true + + # Primary database configuration + primary: + # -- Primary database resource allocation + # resources: + # requests: + # cpu: 2 + # memory: 512Mi + # limits: + # cpu: 3 + # memory: 1024Mi + + # Persistence configuration + persistence: + # -- Enable persistence using PersistentVolumeClaim + enabled: true + # -- Use existing PersistentVolumeClaim + existingClaim: "" + # -- Volume subdirectory to mount + subPath: "" + # -- Storage class for MySQL primary persistent volume + storageClass: "" + # -- Persistent volume claim annotations + annotations: {} + # -- Persistent volume access modes + accessModes: + - ReadWriteOnce + # -- Persistent volume size + size: 8Gi + # -- Selector to match existing Persistent Volume + selector: {} + + # TLS configuration + tls: + # -- Enable TLS support + enabled: false + # -- Secret containing TLS certificates + existingSecret: "" + # -- Certificate filename + certFilename: tls.crt + # -- Certificate key filename + certKeyFilename: tls.key + # -- CA certificate filename + certCAFilename: "" + # -- CA certificate content + ca: "" + # -- Certificate content + cert: "" + # -- Private key content + key: "" + # Automatic certificate generation + autoGenerated: + # -- Enable automatic certificate generation + enabled: true + # -- Certificate generation mechanism (helm, cert-manager) + engine: helm \ No newline at end of file From 845c979fc626f3b8cabbe294354ab41d48f84d33 Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed, 25 Jun 2025 16:52:37 +0200 Subject: [PATCH 22/37] Update CONTRIBUTING.md --- CONTRIBUTING.md | 108 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 79 insertions(+), 29 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b8b5427..f0db023 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,76 +2,126 @@ ## Build and generate template yml locally -``` +```bash helm package ./charts/cadence -helm template cadence-release cadence-0.1.8.tgz > template_out.yaml +# Replace with current chart version +helm template cadence-release cadence-0.2.0.tgz > template_out.yaml ``` ## Build and deploy to a k8s cluster -1. Build helm package and deploy to a k8s cluster +### 1. Update dependencies (if needed) +```bash +cd charts/cadence +helm dependency update +cd ../.. ``` + +### 2. Build helm package and deploy to a k8s cluster +```bash helm package ./charts/cadence -helm upgrade --install cadence-release cadence-0.1.8.tgz \ +# Replace with current chart version +helm upgrade --install cadence-release cadence-0.2.0.tgz \ -n cadencetest \ --create-namespace ``` -2. Port forward to check the UI -``` +### 3. Port forward to check the UI +```bash kubectl port-forward svc/cadence-release-web 8088:8088 -n cadencetest ``` -Visit localhost:8088 and validate it is accessible. +Visit http://localhost:8088 and validate it is accessible. -3. Port forward frontend service to run CLI commands -``` +### 4. Port forward frontend service to run CLI commands +```bash kubectl port-forward svc/cadence-release-frontend 7833:7833 -n cadencetest ``` -4. (optional) Register a domain: -``` +### 5. (Optional) Register a domain: +```bash cadence \ --address localhost:7833 \ --transport grpc \ - --domain samples-domain \ + --domain default \ domain register \ --retention 1 ``` -5. Run samples: -- Clone https://github.com/uber-common/cadence-samples +### 6. Run samples: +- Clone https://github.com/cadence-workflow/cadence-samples -- Run sample worker (run at samples repo root) -``` +- Run sample worker (execute at samples repo root): +```bash ./bin/helloworld -m worker ``` -- Trigger a workflow (run at samples repo root) -``` +- Trigger a workflow (execute at samples repo root): +```bash ./bin/helloworld -m trigger ``` -6. Visit localhost:8088 and validate the new workflow exists! +### 7. Validate deployment +Visit http://localhost:8088 and validate the new workflow exists! -## Generate helmdocs +## Generate helm documentation Install [helm-docs](https://github.com/norwoodj/helm-docs): -``` +```bash go install github.com/norwoodj/helm-docs/cmd/helm-docs@latest ``` -Run it -``` +Generate documentation: +```bash helm-docs ``` -cadencechart/README.md file should be updated. +The `charts/cadence/README.md` file should be updated automatically. + +## Release Process + +### Before making a release: + +1. **Update appVersion and dependencies**: + - Check if Cadence has a new release and update `appVersion` in `Chart.yaml` + - Verify dependencies are using latest-1 stable versions: + - Cassandra: Currently using `11.x.x` from Bitnami + - PostgreSQL: Currently using `16.x.x` from Bitnami + - MySQL: Currently using `12.x.x` from Bitnami + - Update `global.image.tag` in `values.yaml` to match `appVersion` + +2. **Update dependencies**: + ```bash + cd charts/cadence + helm dependency update + cd ../.. + ``` + +3. **Test the changes**: + - Build and deploy locally following the steps above + - Validate all functionality works as expected + +4. **Increment chart version**: Update the `version` field in `charts/cadence/Chart.yaml`: + - Patch version (0.2.1): Bug fixes, dependency updates + - Minor version (0.3.0): New features, breaking changes + - Major version (1.0.0): Major breaking changes + +5. **Update documentation**: Run `helm-docs` to regenerate the README.md + +### Publishing: + +After making changes to templates and incrementing the chart version in `charts/cadence/Chart.yaml`, merge your changes to the main branch. + +The automation will handle publishing the new version using [Chart Releaser Action](https://helm.sh/docs/howto/chart_releaser_action/). The Cadence chart is hosted on GitHub Pages. +After the new version is available in the helm repository, deploy it to a Kubernetes cluster to validate the release. -## Publish chart +## Version Management Checklist -After making changes to templates, increment the chart version in charts/cadence/Chart.yaml. -Then merge your changes and automation will take care of publishing the new version. -Cadence chart is hosted on github pages and automation is done using [Chart Releaser Action](https://helm.sh/docs/howto/chart_releaser_action/). -After new version is available in helm repo, deploy it to a K8s cluster to validate. +When updating versions, ensure consistency across: +- [ ] `Chart.yaml`: `version` (chart version) and `appVersion` (Cadence version) +- [ ] `values.yaml`: `global.image.tag` (should match appVersion) +- [ ] Dependencies in `Chart.yaml` (check for updates, aim for latest-1 stable) +- [ ] Update dependencies with `helm dependency update` +- [ ] Test deployment locally +- [ ] Run `helm-docs` to update documentation \ No newline at end of file From a6c459aa56c699c2205f29dfbda1d748997ad9da Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed, 25 Jun 2025 16:59:34 +0200 Subject: [PATCH 23/37] block rollbacks --- charts/cadence/templates/schema-server-job.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/charts/cadence/templates/schema-server-job.yaml b/charts/cadence/templates/schema-server-job.yaml index 3c21e4a..455e111 100644 --- a/charts/cadence/templates/schema-server-job.yaml +++ b/charts/cadence/templates/schema-server-job.yaml @@ -450,7 +450,7 @@ spec: $(build_cassandra_cmd) -k $DB_NAME setup-schema -v 0.0 || echo "Schema already exists" echo "Updating main schema to latest version" - $(build_cassandra_cmd) -k $DB_NAME update-schema -d $CADENCE_HOME/schema/cassandra/cadence/versioned + $(build_cassandra_cmd) -k $DB_NAME update-schema -d $CADENCE_HOME/schema/cassandra/cadence/versioned || echo "Rollback is not allowed" # Setup visibility database schema (only if ES is not enabled) if [ "$ES_ENABLED" = "false" ]; then @@ -465,7 +465,7 @@ spec: $(build_cassandra_cmd) -k $DB_VISIBILITY_NAME setup-schema -v 0.0 || echo "Schema already exists" echo "Updating visibility schema to latest version" - $(build_cassandra_cmd) -k $DB_VISIBILITY_NAME update-schema -d $CADENCE_HOME/schema/cassandra/visibility/versioned + $(build_cassandra_cmd) -k $DB_VISIBILITY_NAME update-schema -d $CADENCE_HOME/schema/cassandra/visibility/versioned || echo "Rollback is not allowed" else echo "Skipping visibility schema setup (Elasticsearch enabled)" fi @@ -570,7 +570,7 @@ spec: $(build_postgres_cmd) --db $DB_NAME setup-schema -v 0.0 || echo "Schema already exists" echo "Updating main schema to latest version" - $(build_postgres_cmd) --db $DB_NAME update-schema -d $CADENCE_HOME/schema/postgres/cadence/versioned + $(build_postgres_cmd) --db $DB_NAME update-schema -d $CADENCE_HOME/schema/postgres/cadence/versioned || echo "Rollback is not allowed" # Setup visibility database (only if ES is not enabled) if [ "$ES_ENABLED" = "false" ]; then @@ -581,7 +581,7 @@ spec: $(build_postgres_cmd) --db $DB_VISIBILITY_NAME setup-schema -v 0.0 || echo "Schema already exists" echo "Updating visibility schema to latest version" - $(build_postgres_cmd) --db $DB_VISIBILITY_NAME update-schema -d $CADENCE_HOME/schema/postgres/visibility/versioned + $(build_postgres_cmd) --db $DB_VISIBILITY_NAME update-schema -d $CADENCE_HOME/schema/postgres/visibility/versioned || echo "Rollback is not allowed" else echo "Skipping visibility schema setup (Elasticsearch enabled)" fi @@ -675,7 +675,7 @@ spec: $(build_mysql_cmd) --db $DB_NAME setup-schema -v 0.0 || echo "Schema already exists" echo "Updating main schema to latest version" - $(build_mysql_cmd) --db $DB_NAME update-schema -d $CADENCE_HOME/schema/mysql/v8/cadence/versioned + $(build_mysql_cmd) --db $DB_NAME update-schema -d $CADENCE_HOME/schema/mysql/v8/cadence/versioned || echo "Rollback is not allowed" # Setup visibility database (only if ES is not enabled) if [ "$ES_ENABLED" = "false" ]; then @@ -686,7 +686,7 @@ spec: $(build_mysql_cmd) --db $DB_VISIBILITY_NAME setup-schema -v 0.0 || echo "Schema already exists" echo "Updating visibility schema to latest version" - $(build_mysql_cmd) --db $DB_VISIBILITY_NAME update-schema -d $CADENCE_HOME/schema/mysql/v8/visibility/versioned + $(build_mysql_cmd) --db $DB_VISIBILITY_NAME update-schema -d $CADENCE_HOME/schema/mysql/v8/visibility/versioned || echo "Rollback is not allowed" else echo "Skipping visibility schema setup (Elasticsearch enabled)" fi From 880f44bec2b8732821d0a219b99227cbdbc11f5a Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed, 25 Jun 2025 17:02:27 +0200 Subject: [PATCH 24/37] Create schema-elasticsearch-job.yaml --- charts/cadence/templates/schema-elasticsearch-job.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 charts/cadence/templates/schema-elasticsearch-job.yaml diff --git a/charts/cadence/templates/schema-elasticsearch-job.yaml b/charts/cadence/templates/schema-elasticsearch-job.yaml new file mode 100644 index 0000000..e69de29 From 66f3d33b6c066013228d245ad5271197593cf159 Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed, 25 Jun 2025 17:30:57 +0200 Subject: [PATCH 25/37] added servername to job --- charts/cadence/templates/schema-server-job.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/charts/cadence/templates/schema-server-job.yaml b/charts/cadence/templates/schema-server-job.yaml index 455e111..cb4b3cb 100644 --- a/charts/cadence/templates/schema-server-job.yaml +++ b/charts/cadence/templates/schema-server-job.yaml @@ -382,6 +382,10 @@ spec: {{- end }} {{- end }} {{- end }} + volumeMounts: + {{- with $.Values.global.tls.volumeMounts }} + {{- toYaml . | nindent 8 }} + {{- end }} containers: - name: cadence-schema-server {{- $globalImage := .Values.global.image | default dict }} @@ -529,6 +533,8 @@ spec: - name: SSL_CLIENT_KEY value: {{ .Values.config.persistence.database.cassandra.tls.keyFile | quote }} {{- end }} + - name: CASSANDRA_TLS_SERVER_NAME + value: {{ .Values.config.persistence.database.cassandra.tls.serverName | quote }} {{- end }} {{- else if eq $dbDriver "postgres" }} From 42b2ecde80cad30ade836f72fb380643b7c3b31c Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Wed, 25 Jun 2025 17:39:36 +0200 Subject: [PATCH 26/37] change default for tls auto-generated --- charts/cadence/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/cadence/values.yaml b/charts/cadence/values.yaml index f23b273..49efbe8 100644 --- a/charts/cadence/values.yaml +++ b/charts/cadence/values.yaml @@ -835,7 +835,7 @@ config: # -- Require client certificate authentication requireClientAuth: false # -- Override server name for certificate verification - serverName: "cassandra" + serverName: "localhost" # Elasticsearch configuration for advanced visibility elasticsearch: From bffe057e5a843e081492b497b17a4126cf50f4f2 Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu, 26 Jun 2025 10:35:54 +0200 Subject: [PATCH 27/37] auto-detect schema versions --- .../cadence/templates/server-deployment.yaml | 91 +++++++++++++++---- charts/cadence/values.yaml | 8 -- 2 files changed, 75 insertions(+), 24 deletions(-) diff --git a/charts/cadence/templates/server-deployment.yaml b/charts/cadence/templates/server-deployment.yaml index 02775b1..898d74e 100644 --- a/charts/cadence/templates/server-deployment.yaml +++ b/charts/cadence/templates/server-deployment.yaml @@ -1,5 +1,6 @@ {{- range $serviceName := (list "frontend" "history" "matching" "worker") }} {{- $service := index $.Values $serviceName }} +{{- $dbDriver := $.Values.config.persistence.database.driver }} --- apiVersion: apps/v1 kind: Deployment @@ -77,8 +78,56 @@ spec: {{- toYaml $serviceTopologySpreadConstraints | nindent 8 }} {{- end }} initContainers: + - name: extract-schema-versions + {{- $globalImage := $.Values.global.image | default dict }} + {{- $serviceImage := $service.image | default dict }} + {{- $repository := $serviceImage.repository | default $globalImage.repository }} + {{- $tag := $serviceImage.tag | default $globalImage.tag }} + image: {{ $repository }}:{{ $tag }} + {{- $pullPolicy := $serviceImage.pullPolicy | default $globalImage.pullPolicy | default "IfNotPresent" }} + imagePullPolicy: {{ $pullPolicy }} + {{- $globalContainerSecurityContext := $.Values.global.containerSecurityContext | default dict }} + {{- $serviceContainerSecurityContext := $service.containerSecurityContext | default $globalContainerSecurityContext }} + {{- if $serviceContainerSecurityContext }} + securityContext: + {{- toYaml $serviceContainerSecurityContext | nindent 10 }} + {{- end }} + command: ["/bin/sh"] + args: + - -c + - | + # Extraer versiones del archivo version.go + VERSION_FILE="/etc/cadence/schema/cassandra/version.go" + + if [ -f "$VERSION_FILE" ]; then + default_version=$(grep 'const Version' "$VERSION_FILE" | awk -F'"' '{print $2}') + visibility_version=$(grep 'const VisibilityVersion' "$VERSION_FILE" | awk -F'"' '{print $2}') + + echo "DEFAULT_VERSION=$default_version" > /shared/schema-versions.env + echo "VISIBILITY_VERSION=$visibility_version" >> /shared/schema-versions.env + + echo "Extracted versions:" + echo " DEFAULT_VERSION=$default_version" + echo " VISIBILITY_VERSION=$visibility_version" + else + echo "Error: version.go file not found at $VERSION_FILE" + exit 1 + fi + env: + {{- if eq $dbDriver "cassandra" }} + - name: VERSION_FILE + value: "/etc/cadence/schema/cassandra/version.go" + {{- else if eq $dbDriver "postgres" }} + - name: VERSION_FILE + value: "/etc/cadence/schema/postgres/version.go" + {{- else if eq $dbDriver "mysql" }} + - name: VERSION_FILE + value: "/etc/cadence/schema/mysql/version.go" + {{- end }} + volumeMounts: + - name: versions-data + mountPath: /shared - name: wait-for-schema - {{- $dbDriver := $.Values.config.persistence.database.driver }} {{- if eq $dbDriver "cassandra" }} image: {{ $.Values.schema.checkSchema.cassandra.image.repository }}:{{ $.Values.schema.checkSchema.cassandra.image.tag }} imagePullPolicy: {{ $.Values.schema.checkSchema.cassandra.image.pullPolicy }} @@ -86,6 +135,13 @@ spec: - sh - -c - | + # Load extracted versions + source /shared/schema-versions.env + + echo "Using extracted versions:" + echo " DEFAULT_VERSION=$DEFAULT_VERSION" + echo " VISIBILITY_VERSION=$VISIBILITY_VERSION" + # Create .cassandra directory for cqlshrc if it doesn't exist mkdir -p ~/.cassandra @@ -185,11 +241,6 @@ spec: sleep 10 done env: - # Schema version parameters - - name: DEFAULT_VERSION - value: {{ $.Values.schema.version.default | quote }} - - name: VISIBILITY_VERSION - value: {{ $.Values.schema.version.visibility | quote }} - name: ES_ENABLED value: {{ $.Values.config.persistence.elasticsearch.enabled | quote }} # Basic connection parameters @@ -246,6 +297,13 @@ spec: - sh - -c - | + # Load extracted versions + source /shared/schema-versions.env + + echo "Using extracted versions:" + echo " DEFAULT_VERSION=$DEFAULT_VERSION" + echo " VISIBILITY_VERSION=$VISIBILITY_VERSION" + # Build connection string based on TLS configuration build_psql_cmd() { local cmd="psql -h $DB_HOST -p $DB_PORT -U $DB_USER" @@ -302,11 +360,6 @@ spec: echo "PostgreSQL schema is ready!" env: - # Schema version parameters - - name: DEFAULT_VERSION - value: {{ $.Values.schema.version.default | quote }} - - name: VISIBILITY_VERSION - value: {{ $.Values.schema.version.visibility | quote }} - name: ES_ENABLED value: {{ $.Values.config.persistence.elasticsearch.enabled | quote }} # Basic connection parameters @@ -359,6 +412,13 @@ spec: - sh - -c - | + # Load extracted versions + source /shared/schema-versions.env + + echo "Using extracted versions:" + echo " DEFAULT_VERSION=$DEFAULT_VERSION" + echo " VISIBILITY_VERSION=$VISIBILITY_VERSION" + # Build connection string based on TLS configuration build_mysql_cmd() { local cmd="mariadb -h $DB_HOST -P $DB_PORT -u $DB_USER" @@ -433,11 +493,6 @@ spec: echo "MySQL schema is ready!" env: - # Schema version parameters - - name: DEFAULT_VERSION - value: {{ $.Values.schema.version.default | quote }} - - name: VISIBILITY_VERSION - value: {{ $.Values.schema.version.visibility | quote }} - name: ES_ENABLED value: {{ $.Values.config.persistence.elasticsearch.enabled | quote }} # Basic connection parameters @@ -719,6 +774,8 @@ spec: {{- end }} {{- end }} volumeMounts: + - name: versions-data + mountPath: /shared {{- with $.Values.global.tls.volumeMounts }} {{- toYaml . | nindent 8 }} {{- end }} @@ -801,6 +858,8 @@ spec: - name: config configMap: name: {{ include "cadence.fullname" $ }}-configmap + - name: versions-data + emptyDir: {} {{- with $.Values.global.tls.volumes }} {{- toYaml . | nindent 6 }} {{- end }} diff --git a/charts/cadence/values.yaml b/charts/cadence/values.yaml index 49efbe8..4af39c8 100644 --- a/charts/cadence/values.yaml +++ b/charts/cadence/values.yaml @@ -1172,14 +1172,6 @@ schema: # -- ElasticSearch schema job node selector nodeSelector: {} - # Schema version configuration - # TODO: Pending deprecation for auto-detection - version: - # -- Default schema version - default: "0.42" - # -- Visibility schema version - visibility: "0.9" - # -- Container images for schema validation checkSchema: # -- Cassandra schema validation image From 1d0f0c27e168922a0fb9850b88f3674211b17d4b Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu, 26 Jun 2025 10:41:53 +0200 Subject: [PATCH 28/37] change command source and wait for file --- .../cadence/templates/server-deployment.yaml | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/charts/cadence/templates/server-deployment.yaml b/charts/cadence/templates/server-deployment.yaml index 898d74e..bf38263 100644 --- a/charts/cadence/templates/server-deployment.yaml +++ b/charts/cadence/templates/server-deployment.yaml @@ -135,9 +135,14 @@ spec: - sh - -c - | + # Waiting for versions file + while [ ! -f /shared/schema-versions.env ]; do + echo "Waiting for schema versions file..." + sleep 2 + done + # Load extracted versions - source /shared/schema-versions.env - + export $(cat /shared/schema-versions.env | xargs) echo "Using extracted versions:" echo " DEFAULT_VERSION=$DEFAULT_VERSION" echo " VISIBILITY_VERSION=$VISIBILITY_VERSION" @@ -297,9 +302,14 @@ spec: - sh - -c - | - # Load extracted versions - source /shared/schema-versions.env + # Waiting for versions file + while [ ! -f /shared/schema-versions.env ]; do + echo "Waiting for schema versions file..." + sleep 2 + done + # Load extracted versions + export $(cat /shared/schema-versions.env | xargs) echo "Using extracted versions:" echo " DEFAULT_VERSION=$DEFAULT_VERSION" echo " VISIBILITY_VERSION=$VISIBILITY_VERSION" @@ -412,9 +422,14 @@ spec: - sh - -c - | - # Load extracted versions - source /shared/schema-versions.env + # Waiting for versions file + while [ ! -f /shared/schema-versions.env ]; do + echo "Waiting for schema versions file..." + sleep 2 + done + # Load extracted versions + export $(cat /shared/schema-versions.env | xargs) echo "Using extracted versions:" echo " DEFAULT_VERSION=$DEFAULT_VERSION" echo " VISIBILITY_VERSION=$VISIBILITY_VERSION" From b13c7252376ad6e2aeecc8064a754b3021946661 Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu, 26 Jun 2025 10:56:17 +0200 Subject: [PATCH 29/37] remove hardcoded filepath --- charts/cadence/templates/server-deployment.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/charts/cadence/templates/server-deployment.yaml b/charts/cadence/templates/server-deployment.yaml index bf38263..6e93513 100644 --- a/charts/cadence/templates/server-deployment.yaml +++ b/charts/cadence/templates/server-deployment.yaml @@ -96,9 +96,6 @@ spec: args: - -c - | - # Extraer versiones del archivo version.go - VERSION_FILE="/etc/cadence/schema/cassandra/version.go" - if [ -f "$VERSION_FILE" ]; then default_version=$(grep 'const Version' "$VERSION_FILE" | awk -F'"' '{print $2}') visibility_version=$(grep 'const VisibilityVersion' "$VERSION_FILE" | awk -F'"' '{print $2}') From b80fec823052201b2743f5562fcd38c0e581e327 Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu, 26 Jun 2025 12:03:48 +0200 Subject: [PATCH 30/37] fix wait-for-mysql in job --- charts/cadence/templates/schema-server-job.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/charts/cadence/templates/schema-server-job.yaml b/charts/cadence/templates/schema-server-job.yaml index cb4b3cb..cc22552 100644 --- a/charts/cadence/templates/schema-server-job.yaml +++ b/charts/cadence/templates/schema-server-job.yaml @@ -287,7 +287,7 @@ spec: - | # Build connection string based on TLS configuration build_mysql_cmd() { - local cmd="mariadb -h $DB_HOST -P $DB_PORT -u $DB_USER" + local cmd="mariadb -h $DB_HOST -P $DB_PORT -u $DB_USER -p$MYSQL_PWD" # Add SSL parameters if TLS is enabled if [ "$TLS_ENABLED" = "true" ]; then @@ -298,7 +298,7 @@ spec: "preferred") ;; "required"|"true"|"skip-verify") - cmd="$cmd --ssl --ssl-verify-server-cert=false" + cmd="$cmd --ssl --ssl-verify-server-cert=off" ;; "verify-ca") cmd="$cmd --ssl --ssl-verify-server-cert" @@ -323,6 +323,8 @@ spec: if [ -n "$SSL_CLIENT_KEY" ]; then cmd="$cmd --ssl-key=$SSL_CLIENT_KEY" fi + else + cmd="$cmd --skip-ssl" fi echo "$cmd" @@ -330,7 +332,7 @@ spec: # Wait for MySQL to be ready echo "Waiting for MySQL to be ready..." - until mariadb-admin ping -h $DB_HOST -P $DB_PORT -u $DB_USER --password=$MYSQL_PWD --skip-ssl --silent; do + until $(build_mysql_cmd) -e "SELECT 1" >/dev/null 2>&1; do echo 'MySQL is not ready yet...' sleep 5 done From 8431891802df1171d1501955f6e7ba06e060e559 Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu, 26 Jun 2025 12:06:42 +0200 Subject: [PATCH 31/37] handled mysql createdb --- charts/cadence/templates/schema-server-job.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/cadence/templates/schema-server-job.yaml b/charts/cadence/templates/schema-server-job.yaml index cc22552..aa2c928 100644 --- a/charts/cadence/templates/schema-server-job.yaml +++ b/charts/cadence/templates/schema-server-job.yaml @@ -677,7 +677,7 @@ spec: # Create main database echo "Creating main database: $DB_NAME" - $(build_mysql_cmd) create-database --db $DB_NAME + $(build_mysql_cmd) create-database --db $DB_NAME || echo "Database already exists" echo "Setting up main schema version 0.0" $(build_mysql_cmd) --db $DB_NAME setup-schema -v 0.0 || echo "Schema already exists" @@ -688,7 +688,7 @@ spec: # Setup visibility database (only if ES is not enabled) if [ "$ES_ENABLED" = "false" ]; then echo "Creating visibility database: $DB_VISIBILITY_NAME" - $(build_mysql_cmd) create-database --db $DB_VISIBILITY_NAME + $(build_mysql_cmd) create-database --db $DB_VISIBILITY_NAME || echo "Database already exists" echo "Setting up visibility schema version 0.0" $(build_mysql_cmd) --db $DB_VISIBILITY_NAME setup-schema -v 0.0 || echo "Schema already exists" From 974f318068e4e2ed011441953c03caf80e663519 Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu, 26 Jun 2025 12:23:25 +0200 Subject: [PATCH 32/37] comment updated --- charts/cadence/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/cadence/values.yaml b/charts/cadence/values.yaml index 4af39c8..c2fca6b 100644 --- a/charts/cadence/values.yaml +++ b/charts/cadence/values.yaml @@ -751,7 +751,7 @@ config: # -- Enable TLS enabled: false # -- SSL mode (for PostgreSQL: disable, allow, prefer, require, verify-ca, verify-full) - # -- For MySQL: false, true, skip-verify, preferred. (Additional this should work: required, verify-ca, verify-identity) + # -- For MySQL: false, true, skip-verify, preferred sslMode: "" # -- Path to CA certificate file caFile: "" From d17b51670880189849201b82f8dc4cee06622756 Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu, 26 Jun 2025 12:47:30 +0200 Subject: [PATCH 33/37] postgresql wait for db corrected to use tls --- .../cadence/templates/schema-server-job.yaml | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/charts/cadence/templates/schema-server-job.yaml b/charts/cadence/templates/schema-server-job.yaml index aa2c928..64d4dcc 100644 --- a/charts/cadence/templates/schema-server-job.yaml +++ b/charts/cadence/templates/schema-server-job.yaml @@ -200,34 +200,34 @@ spec: - sh - -c - | - # Build connection string based on TLS configuration - build_psql_cmd() { - local cmd="psql -h $DB_HOST -p $DB_PORT -U $DB_USER" + # Set up PostgreSQL environment variables + export PGPASSWORD="$POSTGRES_PWD" + + # Add SSL mode if TLS is enabled + if [ "$TLS_ENABLED" = "true" ] && [ -n "$SSL_MODE" ]; then + # Set SSL mode as environment variable (psql reads PGSSLMODE) + export PGSSLMODE="$SSL_MODE" - # Add SSL mode if TLS is enabled - if [ "$TLS_ENABLED" = "true" ] && [ -n "$SSL_MODE" ]; then - cmd="$cmd --set=sslmode=$SSL_MODE" - - # Add SSL certificate parameters if provided - if [ -n "$SSL_CERTFILE" ]; then - cmd="$cmd --set=sslrootcert=$SSL_CERTFILE" - fi - - if [ -n "$SSL_CLIENT_CERT" ]; then - cmd="$cmd --set=sslcert=$SSL_CLIENT_CERT" - fi - - if [ -n "$SSL_CLIENT_KEY" ]; then - cmd="$cmd --set=sslkey=$SSL_CLIENT_KEY" - fi + # Add SSL certificate parameters as environment variables if provided + if [ -n "$SSL_CERTFILE" ]; then + export PGSSLROOTCERT="$SSL_CERTFILE" fi - echo "$cmd" - } + if [ -n "$SSL_CLIENT_CERT" ]; then + export PGSSLCERT="$SSL_CLIENT_CERT" + fi + + if [ -n "$SSL_CLIENT_KEY" ]; then + export PGSSLKEY="$SSL_CLIENT_KEY" + fi + else + # Disable SSL if TLS is not enabled + export PGSSLMODE="disable" + fi - # Wait for PostgreSQL to be ready + # Wait for PostgreSQL to be ready with authentication and TLS echo "Waiting for PostgreSQL to be ready..." - until pg_isready -h $DB_HOST -p $DB_PORT -U $DB_USER; do + until psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d postgres -c "SELECT 1" >/dev/null 2>&1; do echo 'PostgreSQL is not ready yet...' sleep 5 done From 4eeb88c08a4bf01cc2c0d5568e7e413405803aea Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu, 26 Jun 2025 12:59:15 +0200 Subject: [PATCH 34/37] base for elasticsearch --- .../templates/schema-elasticsearch-job.yaml | 257 ++++++++++++++++++ 1 file changed, 257 insertions(+) diff --git a/charts/cadence/templates/schema-elasticsearch-job.yaml b/charts/cadence/templates/schema-elasticsearch-job.yaml index e69de29..b5b7abc 100644 --- a/charts/cadence/templates/schema-elasticsearch-job.yaml +++ b/charts/cadence/templates/schema-elasticsearch-job.yaml @@ -0,0 +1,257 @@ +{{- if and .Values.schema.elasticSearchJob.enabled .Values.config.persistence.elasticsearch.enabled -}} +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "cadence.fullname" . }}-schema-elasticsearch + labels: + {{- include "cadence.labels" . | nindent 4 }} + app.kubernetes.io/component: schema-elasticsearch +spec: + ttlSecondsAfterFinished: 60 + template: + metadata: + labels: + {{- include "cadence.labels" . | nindent 8 }} + app.kubernetes.io/component: schema-elasticsearch + spec: + {{- if $.Values.serviceAccount.create }} + serviceAccountName: {{ include "cadence.serviceAccountName" $ }} + {{- end }} + {{- $globalImagePullSecrets := $.Values.global.imagePullSecrets | default list }} + {{- $schemaImagePullSecrets := $.Values.frontend.imagePullSecrets | default list }} + {{- $mergedImagePullSecrets := concat $globalImagePullSecrets $schemaImagePullSecrets }} + {{- if $mergedImagePullSecrets }} + imagePullSecrets: + {{- toYaml $mergedImagePullSecrets | nindent 8 }} + {{- end }} + {{- $globalNodeSelector := $.Values.global.nodeSelector | default dict }} + {{- $schemaNodeSelector := $.Values.schema.elasticSearchJob.nodeSelector | default $globalNodeSelector }} + {{- if $schemaNodeSelector }} + nodeSelector: + {{- toYaml $schemaNodeSelector | nindent 8 }} + {{- end }} + {{- $globalAffinity := $.Values.global.affinity | default dict }} + {{- $schemaAffinity := $.Values.schema.elasticSearchJob.affinity | default $globalAffinity }} + {{- if $schemaAffinity }} + affinity: + {{- toYaml $schemaAffinity | nindent 8 }} + {{- end }} + {{- $globalTolerations := $.Values.global.tolerations | default list }} + {{- $schemaTolerations := $.Values.schema.elasticSearchJob.tolerations | default $globalTolerations }} + {{- if $schemaTolerations }} + tolerations: + {{- toYaml $schemaTolerations | nindent 8 }} + {{- end }} + restartPolicy: OnFailure + initContainers: + - name: wait-for-elasticsearch + image: {{ $.Values.schema.checkSchema.elasticsearch.image.repository | default "alpine/curl" }}:{{ $.Values.schema.checkSchema.elasticsearch.image.tag | default "latest" }} + imagePullPolicy: {{ $.Values.schema.checkSchema.elasticsearch.image.pullPolicy | default "IfNotPresent" }} + command: + - sh + - -c + - | + echo "Starting Elasticsearch schema validation..." + + # Build Elasticsearch connection parameters + build_es_connection() { + # Determine protocol - allow override from values or default based on TLS + if [ -n "$ES_PROTOCOL" ]; then + PROTOCOL="$ES_PROTOCOL" + elif [ "$TLS_ENABLED" = "true" ]; then + PROTOCOL="https" + else + PROTOCOL="http" + fi + + # Build curl options for TLS + CURL_OPTS="" + if [ "$TLS_ENABLED" = "true" ]; then + # Configure SSL verification based on host verification setting + if [ "$ENABLE_HOST_VERIFICATION" = "false" ]; then + CURL_OPTS="$CURL_OPTS -k" + fi + + # Add CA certificate if provided + if [ -n "$SSL_CA_FILE" ]; then + CURL_OPTS="$CURL_OPTS --cacert $SSL_CA_FILE" + fi + + # Add client certificate for mutual TLS if provided + if [ -n "$SSL_CLIENT_CERT" ] && [ -n "$SSL_CLIENT_KEY" ]; then + CURL_OPTS="$CURL_OPTS --cert $SSL_CLIENT_CERT --key $SSL_CLIENT_KEY" + fi + + # Override server name if specified + if [ -n "$SSL_SERVER_NAME" ]; then + CURL_OPTS="$CURL_OPTS --resolve $SSL_SERVER_NAME:$ES_PORT:$ES_HOST" + fi + + # Additional TLS options + if [ "$REQUIRE_CLIENT_AUTH" = "true" ]; then + # Client auth is required, ensure we have client cert + if [ -z "$SSL_CLIENT_CERT" ] || [ -z "$SSL_CLIENT_KEY" ]; then + echo "Error: Client authentication required but client certificate/key not provided" + exit 1 + fi + fi + fi + + # Add authentication if user/password provided + if [ -n "$ES_USER" ] && [ -n "$ES_PWD" ]; then + CURL_OPTS="$CURL_OPTS -u $ES_USER:$ES_PWD" + fi + + # Set global variables + BASE_URL="$PROTOCOL://$ES_HOST:$ES_PORT" + + echo "Connecting to Elasticsearch at: $BASE_URL" + echo "TLS Enabled: $TLS_ENABLED" + if [ "$TLS_ENABLED" = "true" ]; then + echo "Host Verification: $ENABLE_HOST_VERIFICATION" + echo "Client Auth Required: $REQUIRE_CLIENT_AUTH" + fi + } + + # Wait for Elasticsearch to be ready + echo "Waiting for Elasticsearch to be ready..." + build_es_connection + + # Check Elasticsearch health + until curl $CURL_OPTS -s -f "$BASE_URL/_cluster/health?wait_for_status=yellow&timeout=5s" > /dev/null; do + echo "Elasticsearch is not ready yet..." + sleep 10 + done + + echo "Elasticsearch is ready!" + env: + # Basic Elasticsearch connection parameters + - name: ES_HOST + value: {{ $.Values.config.persistence.elasticsearch.hosts | quote }} + - name: ES_PORT + value: {{ $.Values.config.persistence.elasticsearch.port | default 9200 | quote }} + - name: ES_PROTOCOL + value: {{ $.Values.config.persistence.elasticsearch.protocol | default "" | quote }} + - name: VISIBILITY_INDEX + value: {{ $.Values.config.persistence.elasticsearch.visibilityIndex | quote }} + - name: ES_VERSION + value: {{ $.Values.config.persistence.elasticsearch.version | quote }} + # Authentication parameters + {{- if $.Values.config.persistence.elasticsearch.user }} + - name: ES_USER + value: {{ $.Values.config.persistence.elasticsearch.user | quote }} + {{- end }} + {{- if $.Values.config.persistence.elasticsearch.password }} + - name: ES_PWD + valueFrom: + secretKeyRef: + name: {{ include "cadence.fullname" $ }}-{{ $serviceName }}-secrets + key: ES_PWD + {{- end }} + # TLS Configuration + - name: TLS_ENABLED + value: {{ $.Values.config.persistence.elasticsearch.tls.enabled | quote }} + {{- if $.Values.config.persistence.elasticsearch.tls.enabled }} + - name: ENABLE_HOST_VERIFICATION + value: {{ $.Values.config.persistence.elasticsearch.tls.enableHostVerification | quote }} + - name: REQUIRE_CLIENT_AUTH + value: {{ $.Values.config.persistence.elasticsearch.tls.requireClientAuth | quote }} + # CA certificate file + {{- if $.Values.config.persistence.elasticsearch.tls.caFile }} + - name: SSL_CA_FILE + value: {{ $.Values.config.persistence.elasticsearch.tls.caFile | quote }} + {{- else if $.Values.config.persistence.elasticsearch.tls.caFiles }} + - name: SSL_CA_FILE + value: {{ index $.Values.config.persistence.elasticsearch.tls.caFiles 0 | quote }} + {{- end }} + # Client certificate for mutual TLS + {{- if $.Values.config.persistence.elasticsearch.tls.certFile }} + - name: SSL_CLIENT_CERT + value: {{ $.Values.config.persistence.elasticsearch.tls.certFile | quote }} + {{- end }} + # Client private key for mutual TLS + {{- if $.Values.config.persistence.elasticsearch.tls.keyFile }} + - name: SSL_CLIENT_KEY + value: {{ $.Values.config.persistence.elasticsearch.tls.keyFile | quote }} + {{- end }} + # Server name override + {{- if $.Values.config.persistence.elasticsearch.tls.serverName }} + - name: SSL_SERVER_NAME + value: {{ $.Values.config.persistence.elasticsearch.tls.serverName | quote }} + {{- end }} + containers: + - name: cadence-schema-elasticsearch + {{- $globalImage := .Values.global.image | default dict }} + {{- $schemaImage := $.Values.frontend.image | default dict }} + {{- $repository := $schemaImage.repository | default $globalImage.repository }} + {{- $tag := $schemaImage.tag | default $globalImage.tag }} + image: {{ $repository }}:{{ $tag }} + {{- $pullPolicy := $schemaImage.pullPolicy | default $globalImage.pullPolicy | default "IfNotPresent" }} + imagePullPolicy: {{ $pullPolicy }} + command: + - sh + - -c + - | + set -e + echo "Starting ElasticSearch schema setup for driver: $DB_DRIVER" + echo "=== Setting up ElasticSearch Schema ===" + + + + env: + # Basic Elasticsearch connection parameters + - name: ES_HOST + value: {{ $.Values.config.persistence.elasticsearch.hosts | quote }} + - name: ES_PORT + value: {{ $.Values.config.persistence.elasticsearch.port | default 9200 | quote }} + - name: ES_PROTOCOL + value: {{ $.Values.config.persistence.elasticsearch.protocol | default "" | quote }} + - name: VISIBILITY_INDEX + value: {{ $.Values.config.persistence.elasticsearch.visibilityIndex | quote }} + - name: ES_VERSION + value: {{ $.Values.config.persistence.elasticsearch.version | quote }} + # Authentication parameters + {{- if $.Values.config.persistence.elasticsearch.user }} + - name: ES_USER + value: {{ $.Values.config.persistence.elasticsearch.user | quote }} + {{- end }} + {{- if $.Values.config.persistence.elasticsearch.password }} + - name: ES_PWD + valueFrom: + secretKeyRef: + name: {{ include "cadence.fullname" $ }}-{{ $serviceName }}-secrets + key: ES_PWD + {{- end }} + # TLS Configuration + - name: TLS_ENABLED + value: {{ $.Values.config.persistence.elasticsearch.tls.enabled | quote }} + {{- if $.Values.config.persistence.elasticsearch.tls.enabled }} + - name: ENABLE_HOST_VERIFICATION + value: {{ $.Values.config.persistence.elasticsearch.tls.enableHostVerification | quote }} + - name: REQUIRE_CLIENT_AUTH + value: {{ $.Values.config.persistence.elasticsearch.tls.requireClientAuth | quote }} + # CA certificate file + {{- if $.Values.config.persistence.elasticsearch.tls.caFile }} + - name: SSL_CA_FILE + value: {{ $.Values.config.persistence.elasticsearch.tls.caFile | quote }} + {{- else if $.Values.config.persistence.elasticsearch.tls.caFiles }} + - name: SSL_CA_FILE + value: {{ index $.Values.config.persistence.elasticsearch.tls.caFiles 0 | quote }} + {{- end }} + # Client certificate for mutual TLS + {{- if $.Values.config.persistence.elasticsearch.tls.certFile }} + - name: SSL_CLIENT_CERT + value: {{ $.Values.config.persistence.elasticsearch.tls.certFile | quote }} + {{- end }} + # Client private key for mutual TLS + {{- if $.Values.config.persistence.elasticsearch.tls.keyFile }} + - name: SSL_CLIENT_KEY + value: {{ $.Values.config.persistence.elasticsearch.tls.keyFile | quote }} + {{- end }} + # Server name override + {{- if $.Values.config.persistence.elasticsearch.tls.serverName }} + - name: SSL_SERVER_NAME + value: {{ $.Values.config.persistence.elasticsearch.tls.serverName | quote }} + {{- end }} +{{- end }} \ No newline at end of file From d29d57b125b3e57c8d178b5d083303125c7e34ab Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu, 26 Jun 2025 15:40:58 +0200 Subject: [PATCH 35/37] idea of ES job --- .../templates/schema-elasticsearch-job.yaml | 220 +++++++++++++++++- 1 file changed, 215 insertions(+), 5 deletions(-) diff --git a/charts/cadence/templates/schema-elasticsearch-job.yaml b/charts/cadence/templates/schema-elasticsearch-job.yaml index b5b7abc..d63a2ee 100644 --- a/charts/cadence/templates/schema-elasticsearch-job.yaml +++ b/charts/cadence/templates/schema-elasticsearch-job.yaml @@ -52,7 +52,7 @@ spec: - sh - -c - | - echo "Starting Elasticsearch schema validation..." + echo "Starting Elasticsearch readiness check..." # Build Elasticsearch connection parameters build_es_connection() { @@ -146,7 +146,7 @@ spec: - name: ES_PWD valueFrom: secretKeyRef: - name: {{ include "cadence.fullname" $ }}-{{ $serviceName }}-secrets + name: {{ include "cadence.fullname" $ }}-frontend-secrets key: ES_PWD {{- end }} # TLS Configuration @@ -180,6 +180,11 @@ spec: - name: SSL_SERVER_NAME value: {{ $.Values.config.persistence.elasticsearch.tls.serverName | quote }} {{- end }} + {{- end }} + volumeMounts: + {{- with .Values.global.tls.volumeMounts }} + {{- toYaml . | nindent 8 }} + {{- end }} containers: - name: cadence-schema-elasticsearch {{- $globalImage := .Values.global.image | default dict }} @@ -194,11 +199,204 @@ spec: - -c - | set -e - echo "Starting ElasticSearch schema setup for driver: $DB_DRIVER" - echo "=== Setting up ElasticSearch Schema ===" + echo "Starting ElasticSearch schema setup..." + echo "=== Installing ElasticSearch Schema ===" + + # Build Elasticsearch connection parameters + build_es_connection() { + # Determine protocol - allow override from values or default based on TLS + if [ -n "$ES_PROTOCOL" ]; then + PROTOCOL="$ES_PROTOCOL" + elif [ "$TLS_ENABLED" = "true" ]; then + PROTOCOL="https" + else + PROTOCOL="http" + fi + + # Build curl options for TLS + CURL_OPTS="" + if [ "$TLS_ENABLED" = "true" ]; then + # Configure SSL verification based on host verification setting + if [ "$ENABLE_HOST_VERIFICATION" = "false" ]; then + CURL_OPTS="$CURL_OPTS -k" + fi + + # Add CA certificate if provided + if [ -n "$SSL_CA_FILE" ]; then + CURL_OPTS="$CURL_OPTS --cacert $SSL_CA_FILE" + fi + + # Add client certificate for mutual TLS if provided + if [ -n "$SSL_CLIENT_CERT" ] && [ -n "$SSL_CLIENT_KEY" ]; then + CURL_OPTS="$CURL_OPTS --cert $SSL_CLIENT_CERT --key $SSL_CLIENT_KEY" + fi + + # Override server name if specified + if [ -n "$SSL_SERVER_NAME" ]; then + CURL_OPTS="$CURL_OPTS --resolve $SSL_SERVER_NAME:$ES_PORT:$ES_HOST" + fi + fi + + # Add authentication if user/password provided + if [ -n "$ES_USER" ] && [ -n "$ES_PWD" ]; then + CURL_OPTS="$CURL_OPTS -u $ES_USER:$ES_PWD" + fi + + # Set global variables + BASE_URL="$PROTOCOL://$ES_HOST:$ES_PORT" + + echo "Connecting to Elasticsearch at: $BASE_URL" + echo "TLS Enabled: $TLS_ENABLED" + if [ "$TLS_ENABLED" = "true" ]; then + echo "Host Verification: $ENABLE_HOST_VERIFICATION" + echo "Client Auth Required: $REQUIRE_CLIENT_AUTH" + fi + } + + # Initialize connection parameters + build_es_connection + + # Determine schema file path based on ES version + case "$ES_VERSION" in + "v6") + SCHEMA_FILE="$CADENCE_HOME/schema/elasticsearch/v6/visibility/index_template.json" + ;; + "v7") + SCHEMA_FILE="$CADENCE_HOME/schema/elasticsearch/v7/visibility/index_template.json" + ;; + *) + echo "Error: Unsupported Elasticsearch version: $ES_VERSION" + echo "Supported versions: v6, v7, v8 (in values should be v7)" + exit 1 + ;; + esac + + echo "Using schema file: $SCHEMA_FILE" + echo "Elasticsearch version: $ES_VERSION" + # Check if schema file exists + if [ ! -f "$SCHEMA_FILE" ]; then + echo "Error: Schema file not found: $SCHEMA_FILE" + echo "Available schema files:" + find $CADENCE_HOME/schema/elasticsearch -name "*.json" -type f || echo "No schema files found" + exit 1 + fi + # Step 1: Install template + echo "Step 1: Installing Cadence visibility template..." + TEMPLATE_URL="$BASE_URL/_template/cadence-visibility-template" + echo "Uploading template to: $TEMPLATE_URL" + TEMPLATE_RESPONSE=$(curl $CURL_OPTS -s -w "%{http_code}" -X PUT "$TEMPLATE_URL" -H 'Content-Type: application/json' --data-binary "@$SCHEMA_FILE") + TEMPLATE_HTTP_CODE=$(echo "$TEMPLATE_RESPONSE" | tail -c 4) + TEMPLATE_BODY=$(echo "$TEMPLATE_RESPONSE" | head -c -4) + + if [ "$TEMPLATE_HTTP_CODE" -eq 200 ] || [ "$TEMPLATE_HTTP_CODE" -eq 201 ]; then + echo "✓ Template installed successfully" + echo "Response: $TEMPLATE_BODY" + else + echo "✗ Failed to install template. HTTP Code: $TEMPLATE_HTTP_CODE" + echo "Response: $TEMPLATE_BODY" + exit 1 + fi + + # Step 2: Create visibility index + echo "Step 2: Creating visibility index..." + INDEX_URL="$BASE_URL/$VISIBILITY_INDEX" + + echo "Creating index: $INDEX_URL" + INDEX_RESPONSE=$(curl $CURL_OPTS -s -w "%{http_code}" -X PUT "$INDEX_URL") + INDEX_HTTP_CODE=$(echo "$INDEX_RESPONSE" | tail -c 4) + INDEX_BODY=$(echo "$INDEX_RESPONSE" | head -c -4) + + if [ "$INDEX_HTTP_CODE" -eq 200 ] || [ "$INDEX_HTTP_CODE" -eq 201 ]; then + echo "✓ Index created successfully" + echo "Response: $INDEX_BODY" + elif [ "$INDEX_HTTP_CODE" -eq 400 ] && echo "$INDEX_BODY" | grep -q "resource_already_exists_exception"; then + echo "✓ Index already exists" + echo "Response: $INDEX_BODY" + else + echo "✗ Failed to create index. HTTP Code: $INDEX_HTTP_CODE" + echo "Response: $INDEX_BODY" + exit 1 + fi + + # Step 3: Verify installation + echo "Step 3: Verifying installation..." + + # Check template exists + echo "Checking template..." + TEMPLATE_CHECK=$(curl $CURL_OPTS -s -f "$TEMPLATE_URL") + if [ $? -eq 0 ]; then + echo "✓ Template verification successful" + if echo "$TEMPLATE_CHECK" | grep -q "cadence-visibility-template"; then + echo "✓ Template structure is valid" + fi + else + echo "✗ Template verification failed" + exit 1 + fi + + # Check index exists and is healthy + echo "Checking index..." + INDEX_CHECK=$(curl $CURL_OPTS -s -f "$INDEX_URL") + if [ $? -eq 0 ]; then + echo "✓ Index verification successful" + + # Get index stats + INDEX_STATS=$(curl $CURL_OPTS -s "$INDEX_URL/_stats") + if [ $? -eq 0 ]; then + echo "✓ Index is healthy and accessible" + # Try to extract document count + DOC_COUNT=$(echo "$INDEX_STATS" | grep -o '"count":[0-9]*' | head -1 | cut -d':' -f2) + if [ -n "$DOC_COUNT" ]; then + echo " - Document count: $DOC_COUNT" + fi + fi + else + echo "✗ Index verification failed" + exit 1 + fi + + # Step 4: Version-specific validation + echo "Step 4: Performing version-specific validation..." + case "$ES_VERSION" in + "v6") + # Check ES6 document type mapping + TYPE_URL="$BASE_URL/$VISIBILITY_INDEX/_mapping/_doc" + TYPE_CHECK=$(curl $CURL_OPTS -s -f "$TYPE_URL") + if [ $? -eq 0 ]; then + echo "✓ ES6 document type mapping exists" + else + echo "✗ ES6 document type mapping check failed" + exit 1 + fi + ;; + "v7") + # Check ES7/8 index mapping + MAPPING_URL="$BASE_URL/$VISIBILITY_INDEX/_mapping" + MAPPING_CHECK=$(curl $CURL_OPTS -s -f "$MAPPING_URL") + if [ $? -eq 0 ]; then + echo "✓ ES7 index mapping exists" + else + echo "✗ ES7 index mapping check failed" + exit 1 + fi + ;; + esac + + # Final summary + echo "" + echo "=== Elasticsearch Schema Installation Summary ===" + echo "Template: Installed ✓" + echo "Index: Created ✓" + echo "Mapping: Verified ✓" + echo "Version: $ES_VERSION" + echo "Schema File: $SCHEMA_FILE" + echo "===============================================" + + echo "Elasticsearch schema installation completed successfully!" + exit 0 env: # Basic Elasticsearch connection parameters - name: ES_HOST @@ -211,6 +409,9 @@ spec: value: {{ $.Values.config.persistence.elasticsearch.visibilityIndex | quote }} - name: ES_VERSION value: {{ $.Values.config.persistence.elasticsearch.version | quote }} + # Cadence home directory + - name: CADENCE_HOME + value: "/etc/cadence" # Authentication parameters {{- if $.Values.config.persistence.elasticsearch.user }} - name: ES_USER @@ -220,7 +421,7 @@ spec: - name: ES_PWD valueFrom: secretKeyRef: - name: {{ include "cadence.fullname" $ }}-{{ $serviceName }}-secrets + name: {{ include "cadence.fullname" $ }}-frontend-secrets key: ES_PWD {{- end }} # TLS Configuration @@ -254,4 +455,13 @@ spec: - name: SSL_SERVER_NAME value: {{ $.Values.config.persistence.elasticsearch.tls.serverName | quote }} {{- end }} + {{- end }} + volumeMounts: + {{- with .Values.global.tls.volumeMounts }} + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + {{- with .Values.global.tls.volumes }} + {{- toYaml . | nindent 6 }} + {{- end }} {{- end }} \ No newline at end of file From 25e2da04ffaaaca5730987ef213f6f7820302e8b Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu, 26 Jun 2025 15:52:47 +0200 Subject: [PATCH 36/37] fix wrong shared --- charts/cadence/templates/server-deployment.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/charts/cadence/templates/server-deployment.yaml b/charts/cadence/templates/server-deployment.yaml index 6e93513..491d8d8 100644 --- a/charts/cadence/templates/server-deployment.yaml +++ b/charts/cadence/templates/server-deployment.yaml @@ -551,6 +551,9 @@ spec: {{- end }} {{- end }} {{- end }} + volumeMounts: + - name: versions-data + mountPath: /shared {{- if $.Values.config.persistence.elasticsearch.enabled }} # ElasticSearch Schema Validation Init Container - name: check-elasticsearch-schema @@ -785,9 +788,6 @@ spec: {{- end }} {{- end }} {{- end }} - volumeMounts: - - name: versions-data - mountPath: /shared {{- with $.Values.global.tls.volumeMounts }} {{- toYaml . | nindent 8 }} {{- end }} From da6fc9467da92058467551d76ab2aa091d9a715c Mon Sep 17 00:00:00 2001 From: CosminL-DEV <82363564+CosminL-DEV@users.noreply.github.com> Date: Thu, 26 Jun 2025 16:08:25 +0200 Subject: [PATCH 37/37] useless env --- charts/cadence/templates/schema-elasticsearch-job.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/charts/cadence/templates/schema-elasticsearch-job.yaml b/charts/cadence/templates/schema-elasticsearch-job.yaml index d63a2ee..02a4d98 100644 --- a/charts/cadence/templates/schema-elasticsearch-job.yaml +++ b/charts/cadence/templates/schema-elasticsearch-job.yaml @@ -133,10 +133,6 @@ spec: value: {{ $.Values.config.persistence.elasticsearch.port | default 9200 | quote }} - name: ES_PROTOCOL value: {{ $.Values.config.persistence.elasticsearch.protocol | default "" | quote }} - - name: VISIBILITY_INDEX - value: {{ $.Values.config.persistence.elasticsearch.visibilityIndex | quote }} - - name: ES_VERSION - value: {{ $.Values.config.persistence.elasticsearch.version | quote }} # Authentication parameters {{- if $.Values.config.persistence.elasticsearch.user }} - name: ES_USER